buffet: Add CommandInstance::ToJson

Method is needed to implement commands status requests from
privetd. Current implementation generates JSON on request.

BUG=brillo:430
TEST=unittest

Change-Id: Iee4a788792a33278c997ad535abe11f26fa7f422
Reviewed-on: https://chromium-review.googlesource.com/262215
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Tested-by: Vitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/buffet/commands/cloud_command_proxy.cc b/buffet/commands/cloud_command_proxy.cc
index 8aa27ba..d3cd5a6 100644
--- a/buffet/commands/cloud_command_proxy.cc
+++ b/buffet/commands/cloud_command_proxy.cc
@@ -7,6 +7,7 @@
 #include "buffet/commands/command_instance.h"
 #include "buffet/commands/prop_constraints.h"
 #include "buffet/commands/prop_types.h"
+#include "buffet/commands/schema_constants.h"
 #include "buffet/device_registration_info.h"
 
 namespace buffet {
@@ -20,20 +21,21 @@
 
 void CloudCommandProxy::OnResultsChanged(const native_types::Object& results) {
   base::DictionaryValue patch;
-  patch.Set("results", TypedValueToJson(results, nullptr).get());
+  patch.Set(commands::attributes::kCommand_Results,
+            TypedValueToJson(results, nullptr).get());
   device_registration_info_->UpdateCommand(command_instance_->GetID(), patch);
 }
 
 void CloudCommandProxy::OnStatusChanged(const std::string& status) {
   base::DictionaryValue patch;
   // TODO(antonm): Change status to state.
-  patch.SetString("state", status);
+  patch.SetString(commands::attributes::kCommand_State, status);
   device_registration_info_->UpdateCommand(command_instance_->GetID(), patch);
 }
 
 void CloudCommandProxy::OnProgressChanged(int progress) {
   base::DictionaryValue patch;
-  patch.SetInteger("progress", progress);
+  patch.SetInteger(commands::attributes::kCommand_Progress, progress);
   // TODO(antonm): Consider batching progress change updates.
   device_registration_info_->UpdateCommand(command_instance_->GetID(), patch);
 }
diff --git a/buffet/commands/command_instance.cc b/buffet/commands/command_instance.cc
index 4b13448..926dcf5 100644
--- a/buffet/commands/command_instance.cc
+++ b/buffet/commands/command_instance.cc
@@ -134,7 +134,6 @@
   }
 
   instance.reset(new CommandInstance(command_name, command_def, parameters));
-  // TODO(antonm): Move command_id to ctor and remove setter.
   std::string command_id;
   if (json->GetStringWithoutPathExpansion(commands::attributes::kCommand_Id,
                                           &command_id)) {
@@ -144,6 +143,21 @@
   return instance;
 }
 
+std::unique_ptr<base::DictionaryValue> CommandInstance::ToJson() const {
+  std::unique_ptr<base::DictionaryValue> json{new base::DictionaryValue};
+
+  json->SetString(commands::attributes::kCommand_Id, id_);
+  json->SetString(commands::attributes::kCommand_Name, name_);
+  json->Set(commands::attributes::kCommand_Parameters,
+            TypedValueToJson(parameters_, nullptr).release());
+  json->Set(commands::attributes::kCommand_Results,
+            TypedValueToJson(results_, nullptr).release());
+  json->SetInteger(commands::attributes::kCommand_Progress, progress_);
+  json->SetString(commands::attributes::kCommand_State, status_);
+
+  return json;
+}
+
 void CommandInstance::AddProxy(std::unique_ptr<CommandProxyInterface> proxy) {
   proxies_.push_back(std::move(proxy));
 }
diff --git a/buffet/commands/command_instance.h b/buffet/commands/command_instance.h
index c925b6c..2b09829 100644
--- a/buffet/commands/command_instance.h
+++ b/buffet/commands/command_instance.h
@@ -65,6 +65,9 @@
       const CommandDictionary& dictionary,
       chromeos::ErrorPtr* error);
 
+  // Returns JSON representation of the command.
+  std::unique_ptr<base::DictionaryValue> ToJson() const;
+
   // Sets the command ID (normally done by CommandQueue when the command
   // instance is added to it).
   void SetID(const std::string& id) { id_ = id; }
diff --git a/buffet/commands/command_instance_unittest.cc b/buffet/commands/command_instance_unittest.cc
index 98c23e5..48d54b9 100644
--- a/buffet/commands/command_instance_unittest.cc
+++ b/buffet/commands/command_instance_unittest.cc
@@ -189,3 +189,33 @@
   EXPECT_EQ("command_failed", error->GetCode());
   EXPECT_EQ("Failed to validate command 'robot.speak'", error->GetMessage());
 }
+
+TEST_F(CommandInstanceTest, ToJson) {
+  auto json = CreateDictionaryValue(R"({
+    'name': 'robot.jump',
+    'parameters': {
+      'height': 53,
+      '_jumpType': '_withKick'
+    },
+    'results': {}
+  })");
+  auto instance = buffet::CommandInstance::FromJson(json.get(), dict_, nullptr);
+  instance->SetProgress(15);
+  instance->SetID("testId");
+  buffet::native_types::Object results;
+  buffet::IntPropType int_prop;
+  results["testResult"] = int_prop.CreateValue(17, nullptr);
+  instance->SetResults(results);
+
+  json->MergeDictionary(CreateDictionaryValue(R"({
+    'id': 'testId',
+    'progress': 15,
+    'state': 'inProgress',
+    'results': {'testResult': 17}
+  })").get());
+
+  auto converted = instance->ToJson();
+  EXPECT_PRED2([](const base::Value& val1, const base::Value& val2) {
+    return val1.Equals(&val2);
+  }, *json, *converted);
+}
diff --git a/buffet/commands/schema_constants.cc b/buffet/commands/schema_constants.cc
index 12cc6b6..3854278 100644
--- a/buffet/commands/schema_constants.cc
+++ b/buffet/commands/schema_constants.cc
@@ -48,6 +48,8 @@
 const char kCommand_Name[] = "name";
 const char kCommand_Parameters[] = "parameters";
 const char kCommand_Results[] = "results";
+const char kCommand_State[] = "state";
+const char kCommand_Progress[] = "progress";
 }  // namespace attributes
 }  // namespace commands
 
diff --git a/buffet/commands/schema_constants.h b/buffet/commands/schema_constants.h
index a686271..450ad37 100644
--- a/buffet/commands/schema_constants.h
+++ b/buffet/commands/schema_constants.h
@@ -52,6 +52,8 @@
 extern const char kCommand_Name[];
 extern const char kCommand_Parameters[];
 extern const char kCommand_Results[];
+extern const char kCommand_State[];
+extern const char kCommand_Progress[];
 }  // namespace attributes
 }  // namespace commands