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())); +}
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc index 0ec3de7..6c21429 100644 --- a/buffet/device_registration_info.cc +++ b/buffet/device_registration_info.cc
@@ -11,6 +11,8 @@ #include <base/json/json_writer.h> #include <base/values.h> +#include "buffet/commands/command_definition.h" +#include "buffet/commands/command_manager.h" #include "buffet/data_encoding.h" #include "buffet/device_registration_storage_keys.h" #include "buffet/http_transport_curl.h" @@ -356,36 +358,18 @@ if (!CheckParam(storage_keys::kServiceURL, service_url_, error)) return std::string(); - std::vector<std::pair<std::string, std::vector<std::string>>> commands = { - {"SetDeviceConfiguration", {"data"}} - }; + std::unique_ptr<base::DictionaryValue> commands = + command_manager_->GetCommandDictionary().GetCommandsAsJson(true, error); + if (!commands) + return std::string(); base::DictionaryValue req_json; - base::ListValue* set_device_configuration_params = new base::ListValue; - base::DictionaryValue* param1 = new base::DictionaryValue; - param1->SetString("name", "data"); - set_device_configuration_params->Append(param1); - - base::ListValue* vendor_commands = new base::ListValue; - for (const auto& pair : commands) { - base::ListValue* params = new base::ListValue; - for (const auto& param_name : pair.second) { - base::DictionaryValue* param = new base::DictionaryValue; - param->SetString("name", param_name); - params->Append(param); - } - base::DictionaryValue* command = new base::DictionaryValue; - command->SetString("name", pair.first); - command->Set("parameter", params); - vendor_commands->Append(command); - } - req_json.SetString("oauthClientId", client_id_); req_json.SetString("deviceDraft.deviceKind", device_kind_); req_json.SetString("deviceDraft.systemName", system_name_); req_json.SetString("deviceDraft.displayName", display_name_); req_json.SetString("deviceDraft.channel.supportedType", "xmpp"); - req_json.Set("deviceDraft.commands.base.vendorCommands", vendor_commands); + req_json.Set("deviceDraft.commandDefs", commands.release()); std::string url = GetServiceURL("registrationTickets", {{"key", api_key_}}); auto resp_json = http::ParseJsonResponse(
diff --git a/buffet/device_registration_info_unittest.cc b/buffet/device_registration_info_unittest.cc index b8059cd..5f5d8ca 100644 --- a/buffet/device_registration_info_unittest.cc +++ b/buffet/device_registration_info_unittest.cc
@@ -8,6 +8,7 @@ #include "buffet/bind_lambda.h" #include "buffet/commands/command_manager.h" +#include "buffet/commands/unittest_utils.h" #include "buffet/device_registration_info.h" #include "buffet/device_registration_storage_keys.h" #include "buffet/http_request.h" @@ -157,50 +158,51 @@ class DeviceRegistrationInfoTest : public ::testing::Test { protected: virtual void SetUp() override { - InitDefaultStorage(&data); - storage = std::make_shared<MemStorage>(); - storage->Save(&data); - transport = std::make_shared<fake::Transport>(); - dev_reg = std::unique_ptr<DeviceRegistrationInfo>( - new DeviceRegistrationInfo(std::make_shared<CommandManager>(), - transport, storage)); + InitDefaultStorage(&data_); + storage_ = std::make_shared<MemStorage>(); + storage_->Save(&data_); + transport_ = std::make_shared<fake::Transport>(); + command_manager_ = std::make_shared<CommandManager>(); + dev_reg_ = std::unique_ptr<DeviceRegistrationInfo>( + new DeviceRegistrationInfo(command_manager_, transport_, storage_)); } - base::DictionaryValue data; - std::shared_ptr<MemStorage> storage; - std::shared_ptr<fake::Transport> transport; - std::unique_ptr<DeviceRegistrationInfo> dev_reg; + base::DictionaryValue data_; + std::shared_ptr<MemStorage> storage_; + std::shared_ptr<fake::Transport> transport_; + std::unique_ptr<DeviceRegistrationInfo> dev_reg_; + std::shared_ptr<CommandManager> command_manager_; }; //////////////////////////////////////////////////////////////////////////////// TEST_F(DeviceRegistrationInfoTest, GetServiceURL) { - EXPECT_TRUE(dev_reg->Load()); - EXPECT_EQ(test_data::kServiceURL, dev_reg->GetServiceURL()); + EXPECT_TRUE(dev_reg_->Load()); + EXPECT_EQ(test_data::kServiceURL, dev_reg_->GetServiceURL()); std::string url = test_data::kServiceURL; url += "registrationTickets"; - EXPECT_EQ(url, dev_reg->GetServiceURL("registrationTickets")); + EXPECT_EQ(url, dev_reg_->GetServiceURL("registrationTickets")); url += "?key="; url += test_data::kApiKey; - EXPECT_EQ(url, dev_reg->GetServiceURL("registrationTickets", { + EXPECT_EQ(url, dev_reg_->GetServiceURL("registrationTickets", { {"key", test_data::kApiKey} })); url += "&restart=true"; - EXPECT_EQ(url, dev_reg->GetServiceURL("registrationTickets", { + EXPECT_EQ(url, dev_reg_->GetServiceURL("registrationTickets", { {"key", test_data::kApiKey}, {"restart", "true"}, })); } TEST_F(DeviceRegistrationInfoTest, GetOAuthURL) { - EXPECT_TRUE(dev_reg->Load()); - EXPECT_EQ(test_data::kOAuthURL, dev_reg->GetOAuthURL()); + EXPECT_TRUE(dev_reg_->Load()); + EXPECT_EQ(test_data::kOAuthURL, dev_reg_->GetOAuthURL()); std::string url = test_data::kOAuthURL; url += "auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fclouddevices&"; url += "redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&"; url += "response_type=code&"; url += "client_id="; url += test_data::kClientId; - EXPECT_EQ(url, dev_reg->GetOAuthURL("auth", { + EXPECT_EQ(url, dev_reg_->GetOAuthURL("auth", { {"scope", "https://www.googleapis.com/auth/clouddevices"}, {"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"}, {"response_type", "code"}, @@ -209,33 +211,33 @@ } TEST_F(DeviceRegistrationInfoTest, CheckRegistration) { - EXPECT_TRUE(dev_reg->Load()); - EXPECT_FALSE(dev_reg->CheckRegistration(nullptr)); - EXPECT_EQ(0, transport->GetRequestCount()); + EXPECT_TRUE(dev_reg_->Load()); + EXPECT_FALSE(dev_reg_->CheckRegistration(nullptr)); + EXPECT_EQ(0, transport_->GetRequestCount()); - SetDefaultDeviceRegistration(&data); - storage->Save(&data); - EXPECT_TRUE(dev_reg->Load()); + SetDefaultDeviceRegistration(&data_); + storage_->Save(&data_); + EXPECT_TRUE(dev_reg_->Load()); - transport->AddHandler(dev_reg->GetOAuthURL("token"), request_type::kPost, - base::Bind(OAuth2Handler)); - transport->ResetRequestCount(); - EXPECT_TRUE(dev_reg->CheckRegistration(nullptr)); - EXPECT_EQ(1, transport->GetRequestCount()); + transport_->AddHandler(dev_reg_->GetOAuthURL("token"), request_type::kPost, + base::Bind(OAuth2Handler)); + transport_->ResetRequestCount(); + EXPECT_TRUE(dev_reg_->CheckRegistration(nullptr)); + EXPECT_EQ(1, transport_->GetRequestCount()); } TEST_F(DeviceRegistrationInfoTest, GetDeviceInfo) { - SetDefaultDeviceRegistration(&data); - storage->Save(&data); - EXPECT_TRUE(dev_reg->Load()); + SetDefaultDeviceRegistration(&data_); + storage_->Save(&data_); + EXPECT_TRUE(dev_reg_->Load()); - transport->AddHandler(dev_reg->GetOAuthURL("token"), request_type::kPost, - base::Bind(OAuth2Handler)); - transport->AddHandler(dev_reg->GetDeviceURL(), request_type::kGet, - base::Bind(DeviceInfoHandler)); - transport->ResetRequestCount(); - auto device_info = dev_reg->GetDeviceInfo(nullptr); - EXPECT_EQ(2, transport->GetRequestCount()); + transport_->AddHandler(dev_reg_->GetOAuthURL("token"), request_type::kPost, + base::Bind(OAuth2Handler)); + transport_->AddHandler(dev_reg_->GetDeviceURL(), request_type::kGet, + base::Bind(DeviceInfoHandler)); + transport_->ResetRequestCount(); + auto device_info = dev_reg_->GetDeviceInfo(nullptr); + EXPECT_EQ(2, transport_->GetRequestCount()); EXPECT_NE(nullptr, device_info.get()); base::DictionaryValue* dict = nullptr; EXPECT_TRUE(device_info->GetAsDictionary(&dict)); @@ -245,20 +247,20 @@ } TEST_F(DeviceRegistrationInfoTest, GetDeviceId) { - SetDefaultDeviceRegistration(&data); - storage->Save(&data); - EXPECT_TRUE(dev_reg->Load()); + SetDefaultDeviceRegistration(&data_); + storage_->Save(&data_); + EXPECT_TRUE(dev_reg_->Load()); - transport->AddHandler(dev_reg->GetOAuthURL("token"), request_type::kPost, - base::Bind(OAuth2Handler)); - transport->AddHandler(dev_reg->GetDeviceURL(), request_type::kGet, - base::Bind(DeviceInfoHandler)); - std::string id = dev_reg->GetDeviceId(nullptr); + transport_->AddHandler(dev_reg_->GetOAuthURL("token"), request_type::kPost, + base::Bind(OAuth2Handler)); + transport_->AddHandler(dev_reg_->GetDeviceURL(), request_type::kGet, + base::Bind(DeviceInfoHandler)); + std::string id = dev_reg_->GetDeviceId(nullptr); EXPECT_EQ(test_data::kDeviceId, id); } TEST_F(DeviceRegistrationInfoTest, StartRegistration) { - EXPECT_TRUE(dev_reg->Load()); + EXPECT_TRUE(dev_reg_->Load()); auto create_ticket = [](const fake::ServerRequest& request, fake::ServerResponse* response) { @@ -272,6 +274,14 @@ EXPECT_EQ(test_data::kClientId, value); EXPECT_TRUE(json->GetString("deviceDraft.deviceKind", &value)); EXPECT_EQ("vendor", value); + base::DictionaryValue* commandDefs = nullptr; + EXPECT_TRUE(json->GetDictionary("deviceDraft.commandDefs", &commandDefs)); + EXPECT_FALSE(commandDefs->empty()); + EXPECT_EQ("{'base':{'reboot':{'parameters':{" + "'delay':{'minimum':10,'type':'integer'}}}}," + "'robot':{'_jump':{'parameters':{" + "'_height':{'type':'integer'}}}}}", + buffet::unittests::ValueToString(commandDefs)); base::DictionaryValue json_resp; json_resp.SetString("id", test_data::kClaimTicketId); @@ -287,11 +297,36 @@ response->ReplyJson(status_code::Ok, &json_resp); }; - transport->AddHandler(dev_reg->GetServiceURL("registrationTickets"), - request_type::kPost, - base::Bind(create_ticket)); + auto json_base = buffet::unittests::CreateDictionaryValue(R"({ + 'base': { + 'reboot': { + 'parameters': {'delay': 'integer'} + }, + 'shutdown': { + 'parameters': {} + } + } + })"); + EXPECT_TRUE(command_manager_->LoadBaseCommands(*json_base, nullptr)); + auto json_cmds = buffet::unittests::CreateDictionaryValue(R"({ + 'base': { + 'reboot': { + 'parameters': {'delay': {'minimum': 10}} + } + }, + 'robot': { + '_jump': { + 'parameters': {'_height': 'integer'} + } + } + })"); + EXPECT_TRUE(command_manager_->LoadCommands(*json_cmds, "", nullptr)); + + transport_->AddHandler(dev_reg_->GetServiceURL("registrationTickets"), + request_type::kPost, + base::Bind(create_ticket)); std::map<std::string, std::shared_ptr<base::Value>> params; - std::string json_resp = dev_reg->StartRegistration(params, nullptr); + std::string json_resp = dev_reg_->StartRegistration(params, nullptr); auto json = std::unique_ptr<base::Value>(base::JSONReader::Read(json_resp)); EXPECT_NE(nullptr, json.get()); base::DictionaryValue* dict = nullptr; @@ -304,26 +339,27 @@ TEST_F(DeviceRegistrationInfoTest, FinishRegistration_NoAuth) { // Test finalizing ticket with no user authorization token. // This assumes that a client would patch in their email separately. - EXPECT_TRUE(dev_reg->Load()); + EXPECT_TRUE(dev_reg_->Load()); // General ticket finalization handler. std::string ticket_url = - dev_reg->GetServiceURL("registrationTickets/" + + dev_reg_->GetServiceURL("registrationTickets/" + std::string(test_data::kClaimTicketId)); - transport->AddHandler(ticket_url + "/finalize", request_type::kPost, - base::Bind(FinalizeTicketHandler)); + transport_->AddHandler(ticket_url + "/finalize", request_type::kPost, + base::Bind(FinalizeTicketHandler)); - transport->AddHandler(dev_reg->GetOAuthURL("token"), request_type::kPost, - base::Bind(OAuth2Handler)); + transport_->AddHandler(dev_reg_->GetOAuthURL("token"), request_type::kPost, + base::Bind(OAuth2Handler)); - storage->reset_save_count(); - DeviceRegistrationInfo::TestHelper::SetTestTicketId(dev_reg.get()); - EXPECT_TRUE(dev_reg->FinishRegistration("", nullptr)); - EXPECT_EQ(1, storage->save_count()); // The device info must have been saved. - EXPECT_EQ(2, transport->GetRequestCount()); + storage_->reset_save_count(); + DeviceRegistrationInfo::TestHelper::SetTestTicketId(dev_reg_.get()); + EXPECT_TRUE(dev_reg_->FinishRegistration("", nullptr)); + EXPECT_EQ(1, + storage_->save_count()); // The device info must have been saved. + EXPECT_EQ(2, transport_->GetRequestCount()); // Validate the device info saved to storage... - auto storage_data = storage->Load(); + auto storage_data = storage_->Load(); base::DictionaryValue* dict = nullptr; EXPECT_TRUE(storage_data->GetAsDictionary(&dict)); std::string value; @@ -347,17 +383,17 @@ TEST_F(DeviceRegistrationInfoTest, FinishRegistration_Auth) { // Test finalizing ticket with user authorization token. - EXPECT_TRUE(dev_reg->Load()); + EXPECT_TRUE(dev_reg_->Load()); // General ticket finalization handler. std::string ticket_url = - dev_reg->GetServiceURL("registrationTickets/" + + dev_reg_->GetServiceURL("registrationTickets/" + std::string(test_data::kClaimTicketId)); - transport->AddHandler(ticket_url + "/finalize", request_type::kPost, - base::Bind(FinalizeTicketHandler)); + transport_->AddHandler(ticket_url + "/finalize", request_type::kPost, + base::Bind(FinalizeTicketHandler)); - transport->AddHandler(dev_reg->GetOAuthURL("token"), request_type::kPost, - base::Bind(OAuth2Handler)); + transport_->AddHandler(dev_reg_->GetOAuthURL("token"), request_type::kPost, + base::Bind(OAuth2Handler)); // Handle patching in the user email onto the device record. auto email_patch_handler = [](const fake::ServerRequest& request, @@ -382,13 +418,14 @@ {"deviceDraft.channel.supportedType", "xmpp"}, }); }; - transport->AddHandler(ticket_url, request_type::kPatch, - base::Bind(email_patch_handler)); + transport_->AddHandler(ticket_url, request_type::kPatch, + base::Bind(email_patch_handler)); - storage->reset_save_count(); - DeviceRegistrationInfo::TestHelper::SetTestTicketId(dev_reg.get()); - EXPECT_TRUE(dev_reg->FinishRegistration(test_data::kUserAccountAuthCode, + storage_->reset_save_count(); + DeviceRegistrationInfo::TestHelper::SetTestTicketId(dev_reg_.get()); + EXPECT_TRUE(dev_reg_->FinishRegistration(test_data::kUserAccountAuthCode, nullptr)); - EXPECT_EQ(1, storage->save_count()); // The device info must have been saved. - EXPECT_EQ(4, transport->GetRequestCount()); + EXPECT_EQ(1, + storage_->save_count()); // The device info must have been saved. + EXPECT_EQ(4, transport_->GetRequestCount()); }