buffet: Add GCD device draft record to device registration

Use the command definitions provided by Command Manager in
device registration record sent to GCD server.

BUG=chromium:396716
TEST=USE=buffet P2_TEST_FILTER="buffet::*" FEATURES=test emerge-link platform2

Change-Id: If9109bfee8862d9252cdb03301a4362492064809
Reviewed-on: https://chromium-review.googlesource.com/210146
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/commands/command_dictionary.cc b/buffet/commands/command_dictionary.cc
index dcba8c9..e011a23 100644
--- a/buffet/commands/command_dictionary.cc
+++ b/buffet/commands/command_dictionary.cc
@@ -145,6 +145,34 @@
   return true;
 }
 
+std::unique_ptr<base::DictionaryValue> CommandDictionary::GetCommandsAsJson(
+    bool full_schema, ErrorPtr* error) const {
+  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+  for (const auto& pair : definitions_) {
+    std::unique_ptr<base::DictionaryValue> definition =
+        pair.second->GetParameters()->ToJson(full_schema, error);
+    if (!definition) {
+      dict.reset();
+      return dict;
+    }
+    auto cmd_name_parts = string_utils::SplitAtFirst(pair.first, '.');
+    std::string package_name = cmd_name_parts.first;
+    std::string command_name = cmd_name_parts.second;
+    base::DictionaryValue* package = nullptr;
+    if (!dict->GetDictionaryWithoutPathExpansion(package_name, &package)) {
+      // If this is the first time we encounter this package, create a JSON
+      // object for it.
+      package = new base::DictionaryValue;
+      dict->SetWithoutPathExpansion(package_name, package);
+    }
+    base::DictionaryValue* command_def = new base::DictionaryValue;
+    command_def->SetWithoutPathExpansion(
+        commands::attributes::kCommand_Parameters, definition.release());
+    package->SetWithoutPathExpansion(command_name, command_def);
+  }
+  return dict;
+}
+
 const CommandDefinition* CommandDictionary::FindCommand(
     const std::string& command_name) const {
   auto pair = definitions_.find(command_name);
diff --git a/buffet/commands/command_dictionary.h b/buffet/commands/command_dictionary.h
index 78f2cee..b656684 100644
--- a/buffet/commands/command_dictionary.h
+++ b/buffet/commands/command_dictionary.h
@@ -54,6 +54,13 @@
   bool LoadCommands(const base::DictionaryValue& json,
                     const std::string& category,
                     const CommandDictionary* base_commands, ErrorPtr* error);
+  // Converts all the command definitions to a JSON object for CDD/Device
+  // draft. |full_schema| specifies whether full command definitions must
+  // be generated (true) for CDD or only overrides from the base schema (false).
+  // Returns empty unique_ptr in case of an error and fills in the additional
+  // error details in |error|.
+  std::unique_ptr<base::DictionaryValue> GetCommandsAsJson(
+      bool full_schema, ErrorPtr* error) const;
   // Returns the number of command definitions in the dictionary.
   size_t GetSize() const { return definitions_.size(); }
   // Checks if the dictionary has no command definitions.
diff --git a/buffet/commands/command_dictionary_unittest.cc b/buffet/commands/command_dictionary_unittest.cc
index 0aeab50..30d185f 100644
--- a/buffet/commands/command_dictionary_unittest.cc
+++ b/buffet/commands/command_dictionary_unittest.cc
@@ -165,3 +165,46 @@
             error->GetFirstError()->GetMessage());
   error.reset();
 }
+
+TEST(CommandDictionary, GetCommandsAsJson) {
+  auto json_base = CreateDictionaryValue(R"({
+    'base': {
+      'reboot': {
+        'parameters': {'delay': {'maximum': 100}}
+      },
+      'shutdown': {
+        'parameters': {}
+      }
+    }
+  })");
+  buffet::CommandDictionary base_dict;
+  base_dict.LoadCommands(*json_base, "base", nullptr, nullptr);
+
+  auto json = buffet::unittests::CreateDictionaryValue(R"({
+    'base': {
+      'reboot': {
+        'parameters': {'delay': {'minimum': 10}}
+      }
+    },
+    'robot': {
+      '_jump': {
+        'parameters': {'_height': 'integer'}
+      }
+    }
+  })");
+  buffet::CommandDictionary dict;
+  dict.LoadCommands(*json, "device", &base_dict, nullptr);
+
+  json = dict.GetCommandsAsJson(false, nullptr);
+  EXPECT_NE(nullptr, json.get());
+  EXPECT_EQ("{'base':{'reboot':{'parameters':{'delay':{'minimum':10}}}},"
+            "'robot':{'_jump':{'parameters':{'_height':'integer'}}}}",
+            buffet::unittests::ValueToString(json.get()));
+
+  json = dict.GetCommandsAsJson(true, nullptr);
+  EXPECT_NE(nullptr, json.get());
+  EXPECT_EQ("{'base':{'reboot':{'parameters':{'delay':{"
+            "'maximum':100,'minimum':10,'type':'integer'}}}},"
+            "'robot':{'_jump':{'parameters':{'_height':{'type':'integer'}}}}}",
+            buffet::unittests::ValueToString(json.get()));
+}