buffet: Add 'origin' property to Command
Now it is possible to distinguish commands that came from local
clients from those that were sent from the cloud.
The vendor applications could make a decision on how to process
the command depending on where it came from. Also, the command
status update code could do different things depending on whether
status updates need to go to the cloud or the local client.
BUG=None
TEST=`FEATURES=test emerge-link buffet`
Change-Id: I80156c5b43dace0b79b87b86618bfec017b30de9
Reviewed-on: https://chromium-review.googlesource.com/266563
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Trybot-Ready: Alex Vakulenko <avakulenko@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/buffet/commands/command_instance.cc b/buffet/commands/command_instance.cc
index c67411f..5497922 100644
--- a/buffet/commands/command_instance.cc
+++ b/buffet/commands/command_instance.cc
@@ -28,9 +28,11 @@
const char CommandInstance::kStatusExpired[] = "expired";
CommandInstance::CommandInstance(const std::string& name,
+ const std::string& origin,
const CommandDefinition* command_definition,
const native_types::Object& parameters)
: name_{name},
+ origin_{origin},
command_definition_{command_definition},
parameters_{parameters} {
CHECK(command_definition_);
@@ -63,8 +65,7 @@
base::DictionaryValue no_params; // Placeholder when no params are specified.
const base::DictionaryValue* params = nullptr;
const base::Value* params_value = nullptr;
- if (json->GetWithoutPathExpansion(commands::attributes::kCommand_Parameters,
- ¶ms_value)) {
+ if (json->Get(commands::attributes::kCommand_Parameters, ¶ms_value)) {
// Make sure the "parameters" property is actually an object.
if (!params_value->GetAsDictionary(¶ms)) {
chromeos::Error::AddToPrintf(error, FROM_HERE,
@@ -93,6 +94,7 @@
std::unique_ptr<CommandInstance> CommandInstance::FromJson(
const base::Value* value,
+ const std::string& origin,
const CommandDictionary& dictionary,
chromeos::ErrorPtr* error) {
std::unique_ptr<CommandInstance> instance;
@@ -107,8 +109,7 @@
// Get the command name from 'name' property.
std::string command_name;
- if (!json->GetStringWithoutPathExpansion(commands::attributes::kCommand_Name,
- &command_name)) {
+ if (!json->GetString(commands::attributes::kCommand_Name, &command_name)) {
chromeos::Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kPropertyMissing,
"Command name is missing");
@@ -133,10 +134,10 @@
return instance;
}
- instance.reset(new CommandInstance(command_name, command_def, parameters));
+ instance.reset(
+ new CommandInstance{command_name, origin, command_def, parameters});
std::string command_id;
- if (json->GetStringWithoutPathExpansion(commands::attributes::kCommand_Id,
- &command_id)) {
+ if (json->GetString(commands::attributes::kCommand_Id, &command_id)) {
instance->SetID(command_id);
}
diff --git a/buffet/commands/command_instance.h b/buffet/commands/command_instance.h
index e5e0f9f..db9c519 100644
--- a/buffet/commands/command_instance.h
+++ b/buffet/commands/command_instance.h
@@ -33,6 +33,7 @@
// be in format "<package_name>.<command_name>", a command |category| and
// a list of parameters and their values specified in |parameters|.
CommandInstance(const std::string& name,
+ const std::string& origin,
const CommandDefinition* command_definition,
const native_types::Object& parameters);
~CommandInstance();
@@ -50,6 +51,8 @@
// Finds a command parameter value by parameter |name|. If the parameter
// with given name does not exist, returns nullptr.
const PropValue* FindParameter(const std::string& name) const;
+ // Returns the full name of the command.
+ const std::string& GetOrigin() const { return origin_; }
// Returns command definition.
const CommandDefinition* GetCommandDefinition() const {
@@ -62,6 +65,7 @@
// fills in error details in |error|.
static std::unique_ptr<CommandInstance> FromJson(
const base::Value* value,
+ const std::string& origin,
const CommandDictionary& dictionary,
chromeos::ErrorPtr* error);
@@ -121,6 +125,8 @@
std::string id_;
// Full command name as "<package_name>.<command_name>".
std::string name_;
+ // The origin of the command, either "local" or "cloud".
+ std::string origin_;
// Command definition.
const CommandDefinition* command_definition_;
// Command parameters and their values.
diff --git a/buffet/commands/command_instance_unittest.cc b/buffet/commands/command_instance_unittest.cc
index e1d874b..55731b4 100644
--- a/buffet/commands/command_instance_unittest.cc
+++ b/buffet/commands/command_instance_unittest.cc
@@ -73,9 +73,10 @@
params["phrase"] = str_prop.CreateValue(std::string("iPityDaFool"),
nullptr);
params["volume"] = int_prop.CreateValue(5, nullptr);
- buffet::CommandInstance instance("robot.speak",
+ buffet::CommandInstance instance{"robot.speak",
+ "cloud",
dict_.FindCommand("robot.speak"),
- params);
+ params};
buffet::native_types::Object results;
results["foo"] = int_prop.CreateValue(239, nullptr);
@@ -84,18 +85,26 @@
EXPECT_EQ("", instance.GetID());
EXPECT_EQ("robot.speak", instance.GetName());
EXPECT_EQ("robotd", instance.GetCategory());
+ EXPECT_EQ("cloud", instance.GetOrigin());
EXPECT_EQ(params, instance.GetParameters());
EXPECT_EQ("iPityDaFool",
instance.FindParameter("phrase")->GetString()->GetValue());
EXPECT_EQ(5, instance.FindParameter("volume")->GetInt()->GetValue());
EXPECT_EQ(nullptr, instance.FindParameter("blah"));
EXPECT_EQ(results, instance.GetResults());
+
+ buffet::CommandInstance instance2{"base.reboot",
+ "local",
+ dict_.FindCommand("base.reboot"),
+ {}};
+ EXPECT_EQ("local", instance2.GetOrigin());
}
TEST_F(CommandInstanceTest, SetID) {
- buffet::CommandInstance instance("robot._beep",
- dict_.FindCommand("robot.speak"),
- {});
+ buffet::CommandInstance instance{"base.reboot",
+ "local",
+ dict_.FindCommand("base.reboot"),
+ {}};
instance.SetID("command_id");
EXPECT_EQ("command_id", instance.GetID());
}
@@ -109,7 +118,8 @@
},
'results': {}
})");
- auto instance = buffet::CommandInstance::FromJson(json.get(), dict_, nullptr);
+ auto instance = buffet::CommandInstance::FromJson(json.get(), "cloud", dict_,
+ nullptr);
EXPECT_EQ("robot.jump", instance->GetName());
EXPECT_EQ("robotd", instance->GetCategory());
EXPECT_EQ(53, instance->FindParameter("height")->GetInt()->GetValue());
@@ -119,7 +129,8 @@
TEST_F(CommandInstanceTest, FromJson_ParamsOmitted) {
auto json = CreateDictionaryValue("{'name': 'base.reboot'}");
- auto instance = buffet::CommandInstance::FromJson(json.get(), dict_, nullptr);
+ auto instance = buffet::CommandInstance::FromJson(json.get(), "cloud", dict_,
+ nullptr);
EXPECT_EQ("base.reboot", instance->GetName());
EXPECT_EQ("robotd", instance->GetCategory());
EXPECT_TRUE(instance->GetParameters().empty());
@@ -128,7 +139,8 @@
TEST_F(CommandInstanceTest, FromJson_NotObject) {
auto json = CreateValue("'string'");
chromeos::ErrorPtr error;
- auto instance = buffet::CommandInstance::FromJson(json.get(), dict_, &error);
+ auto instance = buffet::CommandInstance::FromJson(json.get(), "cloud", dict_,
+ &error);
EXPECT_EQ(nullptr, instance.get());
EXPECT_EQ("json_object_expected", error->GetCode());
EXPECT_EQ("Command instance is not a JSON object", error->GetMessage());
@@ -137,7 +149,8 @@
TEST_F(CommandInstanceTest, FromJson_NameMissing) {
auto json = CreateDictionaryValue("{'param': 'value'}");
chromeos::ErrorPtr error;
- auto instance = buffet::CommandInstance::FromJson(json.get(), dict_, &error);
+ auto instance = buffet::CommandInstance::FromJson(json.get(), "cloud", dict_,
+ &error);
EXPECT_EQ(nullptr, instance.get());
EXPECT_EQ("parameter_missing", error->GetCode());
EXPECT_EQ("Command name is missing", error->GetMessage());
@@ -146,7 +159,8 @@
TEST_F(CommandInstanceTest, FromJson_UnknownCommand) {
auto json = CreateDictionaryValue("{'name': 'robot.scream'}");
chromeos::ErrorPtr error;
- auto instance = buffet::CommandInstance::FromJson(json.get(), dict_, &error);
+ auto instance = buffet::CommandInstance::FromJson(json.get(), "cloud", dict_,
+ &error);
EXPECT_EQ(nullptr, instance.get());
EXPECT_EQ("invalid_command_name", error->GetCode());
EXPECT_EQ("Unknown command received: robot.scream", error->GetMessage());
@@ -158,7 +172,8 @@
'parameters': 'hello'
})");
chromeos::ErrorPtr error;
- auto instance = buffet::CommandInstance::FromJson(json.get(), dict_, &error);
+ auto instance = buffet::CommandInstance::FromJson(json.get(), "cloud", dict_,
+ &error);
EXPECT_EQ(nullptr, instance.get());
auto inner = error->GetInnerError();
EXPECT_EQ("json_object_expected", inner->GetCode());
@@ -176,7 +191,8 @@
}
})");
chromeos::ErrorPtr error;
- auto instance = buffet::CommandInstance::FromJson(json.get(), dict_, &error);
+ auto instance = buffet::CommandInstance::FromJson(json.get(), "cloud", dict_,
+ &error);
EXPECT_EQ(nullptr, instance.get());
auto first = error->GetFirstError();
EXPECT_EQ("out_of_range", first->GetCode());
@@ -199,7 +215,8 @@
},
'results': {}
})");
- auto instance = buffet::CommandInstance::FromJson(json.get(), dict_, nullptr);
+ auto instance = buffet::CommandInstance::FromJson(json.get(), "cloud", dict_,
+ nullptr);
instance->SetProgress(15);
instance->SetID("testId");
buffet::native_types::Object results;
diff --git a/buffet/commands/command_queue_unittest.cc b/buffet/commands/command_queue_unittest.cc
index 3e4b6ab..ba152b7 100644
--- a/buffet/commands/command_queue_unittest.cc
+++ b/buffet/commands/command_queue_unittest.cc
@@ -23,7 +23,7 @@
const std::string& name,
const std::string& id) {
std::unique_ptr<CommandInstance> cmd{
- new CommandInstance{name, &command_definition_, {}}};
+ new CommandInstance{name, "local", &command_definition_, {}}};
cmd->SetID(id);
return cmd;
}
diff --git a/buffet/commands/dbus_command_dispatcher_unittest.cc b/buffet/commands/dbus_command_dispatcher_unittest.cc
index 1e27aac..313bc28 100644
--- a/buffet/commands/dbus_command_dispatcher_unittest.cc
+++ b/buffet/commands/dbus_command_dispatcher_unittest.cc
@@ -104,7 +104,8 @@
void AddNewCommand(const std::string& json, const std::string& id) {
auto command_instance = CommandInstance::FromJson(
- CreateDictionaryValue(json.c_str()).get(), dictionary_, nullptr);
+ CreateDictionaryValue(json.c_str()).get(), "cloud", dictionary_,
+ nullptr);
command_instance->SetID(id);
// Two interfaces are added - Command and Properties.
EXPECT_CALL(*mock_exported_object_manager_, SendSignal(_)).Times(2);
diff --git a/buffet/commands/dbus_command_proxy.cc b/buffet/commands/dbus_command_proxy.cc
index b300fe3..585d4b4 100644
--- a/buffet/commands/dbus_command_proxy.cc
+++ b/buffet/commands/dbus_command_proxy.cc
@@ -36,6 +36,7 @@
dbus_adaptor_.SetId(command_instance_->GetID());
dbus_adaptor_.SetStatus(command_instance_->GetStatus());
dbus_adaptor_.SetProgress(command_instance_->GetProgress());
+ dbus_adaptor_.SetOrigin(command_instance_->GetOrigin());
dbus_adaptor_.SetParameters(ObjectToDBusVariant(
command_instance_->GetParameters()));
diff --git a/buffet/commands/dbus_command_proxy_unittest.cc b/buffet/commands/dbus_command_proxy_unittest.cc
index ff351ba..45d4b78 100644
--- a/buffet/commands/dbus_command_proxy_unittest.cc
+++ b/buffet/commands/dbus_command_proxy_unittest.cc
@@ -85,7 +85,8 @@
'_jumpType': '_withKick'
}
})");
- command_instance_ = CommandInstance::FromJson(json.get(), dict_, nullptr);
+ command_instance_ =
+ CommandInstance::FromJson(json.get(), "local", dict_, nullptr);
command_instance_->SetID(kTestCommandId);
// Set up a mock ExportedObject to be used with the DBus command proxy.
diff --git a/buffet/dbus_bindings/org.chromium.Buffet.Command.xml b/buffet/dbus_bindings/org.chromium.Buffet.Command.xml
index f5f24ef..89481ec 100644
--- a/buffet/dbus_bindings/org.chromium.Buffet.Command.xml
+++ b/buffet/dbus_bindings/org.chromium.Buffet.Command.xml
@@ -37,5 +37,11 @@
<property name="Progress" type="i" access="read"/>
<property name="Parameters" type="a{sv}" access="read"/>
<property name="Results" type="a{sv}" access="read"/>
+ <property name="Origin" type="s" access="read">
+ <tp:docstring>
+ Specifies the origin of the command. This is a string containing
+ "cloud" or "local" indicating the method of delivery of the command.
+ </tp:docstring>
+ </property>
</interface>
</node>
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index d91e27c..71cd343 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -25,6 +25,7 @@
#include "buffet/commands/cloud_command_proxy.h"
#include "buffet/commands/command_definition.h"
#include "buffet/commands/command_manager.h"
+#include "buffet/commands/schema_constants.h"
#include "buffet/device_registration_storage_keys.h"
#include "buffet/org.chromium.Buffet.Manager.h"
#include "buffet/states/state_manager.h"
@@ -899,8 +900,9 @@
continue;
}
- std::unique_ptr<CommandInstance> command_instance =
- CommandInstance::FromJson(command, command_dictionary, nullptr);
+ auto command_instance = CommandInstance::FromJson(
+ command, commands::attributes::kCommand_Visibility_Cloud,
+ command_dictionary, nullptr);
if (!command_instance) {
LOG(WARNING) << "Failed to parse a command";
continue;
diff --git a/buffet/manager.cc b/buffet/manager.cc
index b83ccb9..897c8a4 100644
--- a/buffet/manager.cc
+++ b/buffet/manager.cc
@@ -183,7 +183,8 @@
}
chromeos::ErrorPtr error;
auto command_instance = buffet::CommandInstance::FromJson(
- value.get(), command_manager_->GetCommandDictionary(), &error);
+ value.get(), commands::attributes::kCommand_Visibility_Local,
+ command_manager_->GetCommandDictionary(), &error);
if (!command_instance) {
response->ReplyWithError(error.get());
return;
diff --git a/buffet/test_daemon/main.cc b/buffet/test_daemon/main.cc
index c87eff8..c208cd8 100644
--- a/buffet/test_daemon/main.cc
+++ b/buffet/test_daemon/main.cc
@@ -73,8 +73,10 @@
void Daemon::OnBuffetCommand(org::chromium::Buffet::CommandProxy* command) {
// "Handle" only commands that belong to this daemon's category.
- if (command->category() != kTestCommandCategory)
+ if (command->category() != kTestCommandCategory ||
+ command->status() == "done") {
return;
+ }
command->SetPropertyChangedCallback(base::Bind(&Daemon::OnPropertyChange,
base::Unretained(this)));
@@ -84,6 +86,7 @@
printf(" category: %s\n", command->category().c_str());
printf(" ID: %s\n", command->id().c_str());
printf(" status: %s\n", command->status().c_str());
+ printf(" origin: %s\n", command->origin().c_str());
printf(" # of parameters: %" PRIuS "\n", command->parameters().size());
auto keys = chromeos::GetMapKeysAsVector(command->parameters());
std::string param_names = chromeos::string_utils::Join(", ", keys);