blob: e62e0483dd5257b32d1d1f2fd36aca0c04dd23c8 [file] [log] [blame]
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -07001// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "buffet/commands/command_instance.h"
6
Alex Vakulenko8dc69af2014-08-07 10:29:42 -07007#include <base/values.h>
Alex Vakulenkoa8b95bc2014-08-27 11:00:57 -07008#include <chromeos/errors/error.h>
9#include <chromeos/errors/error_codes.h>
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070010
11#include "buffet/commands/command_definition.h"
12#include "buffet/commands/command_dictionary.h"
Alex Vakulenkof6b38712014-09-03 16:23:38 -070013#include "buffet/commands/command_proxy_interface.h"
14#include "buffet/commands/command_queue.h"
Alex Vakulenkod94656e2015-03-18 09:54:37 -070015#include "buffet/commands/prop_types.h"
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070016#include "buffet/commands/schema_constants.h"
17#include "buffet/commands/schema_utils.h"
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070018
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070019namespace buffet {
20
Alex Vakulenkof6b38712014-09-03 16:23:38 -070021const char CommandInstance::kStatusQueued[] = "queued";
22const char CommandInstance::kStatusInProgress[] = "inProgress";
23const char CommandInstance::kStatusPaused[] = "paused";
24const char CommandInstance::kStatusError[] = "error";
25const char CommandInstance::kStatusDone[] = "done";
Alex Vakulenkodb221242015-03-13 14:02:46 -070026const char CommandInstance::kStatusCancelled[] = "cancelled";
Alex Vakulenkof6b38712014-09-03 16:23:38 -070027const char CommandInstance::kStatusAborted[] = "aborted";
28const char CommandInstance::kStatusExpired[] = "expired";
29
Alex Vakulenko5ef75792015-03-19 15:50:44 -070030CommandInstance::CommandInstance(const std::string& name,
Alex Vakulenkof784e212015-04-20 12:33:52 -070031 const std::string& origin,
Alex Vakulenko5ef75792015-03-19 15:50:44 -070032 const CommandDefinition* command_definition,
33 const native_types::Object& parameters)
34 : name_{name},
Alex Vakulenkof784e212015-04-20 12:33:52 -070035 origin_{origin},
Alex Vakulenko5ef75792015-03-19 15:50:44 -070036 command_definition_{command_definition},
37 parameters_{parameters} {
38 CHECK(command_definition_);
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070039}
40
Anton Muhinb66a9302014-11-10 22:15:22 +040041CommandInstance::~CommandInstance() = default;
42
Anton Muhincfde8692014-11-25 03:36:59 +040043const std::string& CommandInstance::GetCategory() const {
44 return command_definition_->GetCategory();
45}
46
Alex Vakulenko5ef75792015-03-19 15:50:44 -070047const PropValue* CommandInstance::FindParameter(const std::string& name) const {
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070048 auto p = parameters_.find(name);
Alex Vakulenko5ef75792015-03-19 15:50:44 -070049 return (p != parameters_.end()) ? p->second.get() : nullptr;
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070050}
51
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070052namespace {
53
54// Helper method to retrieve command parameters from the command definition
55// object passed in as |json| and corresponding command definition schema
56// specified in |command_def|.
57// On success, returns |true| and the validated parameters and values through
58// |parameters|. Otherwise returns |false| and additional error information in
59// |error|.
60bool GetCommandParameters(const base::DictionaryValue* json,
61 const CommandDefinition* command_def,
62 native_types::Object* parameters,
Alex Vakulenko5f472062014-08-14 17:54:04 -070063 chromeos::ErrorPtr* error) {
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070064 // Get the command parameters from 'parameters' property.
65 base::DictionaryValue no_params; // Placeholder when no params are specified.
66 const base::DictionaryValue* params = nullptr;
67 const base::Value* params_value = nullptr;
Alex Vakulenkof784e212015-04-20 12:33:52 -070068 if (json->Get(commands::attributes::kCommand_Parameters, &params_value)) {
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070069 // Make sure the "parameters" property is actually an object.
70 if (!params_value->GetAsDictionary(&params)) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -080071 chromeos::Error::AddToPrintf(error, FROM_HERE,
72 chromeos::errors::json::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -070073 chromeos::errors::json::kObjectExpected,
74 "Property '%s' must be a JSON object",
75 commands::attributes::kCommand_Parameters);
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070076 return false;
77 }
78 } else {
79 // "parameters" are not specified. Assume empty param list.
80 params = &no_params;
81 }
82
83 // Now read in the parameters and validate their values against the command
84 // definition schema.
Alex Vakulenkod94656e2015-03-18 09:54:37 -070085 ObjectPropType obj_prop_type;
Alex Vakulenko5ef75792015-03-19 15:50:44 -070086 obj_prop_type.SetObjectSchema(command_def->GetParameters()->Clone());
Alex Vakulenkod94656e2015-03-18 09:54:37 -070087 if (!TypedValueFromJson(params, &obj_prop_type, parameters, error)) {
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070088 return false;
89 }
90 return true;
91}
92
93} // anonymous namespace
94
Alex Vakulenkofedc4872014-08-20 12:38:43 -070095std::unique_ptr<CommandInstance> CommandInstance::FromJson(
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070096 const base::Value* value,
Alex Vakulenkof784e212015-04-20 12:33:52 -070097 const std::string& origin,
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070098 const CommandDictionary& dictionary,
Alex Vakulenko5f472062014-08-14 17:54:04 -070099 chromeos::ErrorPtr* error) {
Alex Vakulenkofedc4872014-08-20 12:38:43 -0700100 std::unique_ptr<CommandInstance> instance;
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700101 // Get the command JSON object from the value.
102 const base::DictionaryValue* json = nullptr;
103 if (!value->GetAsDictionary(&json)) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800104 chromeos::Error::AddTo(error, FROM_HERE, chromeos::errors::json::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700105 chromeos::errors::json::kObjectExpected,
106 "Command instance is not a JSON object");
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700107 return instance;
108 }
109
110 // Get the command name from 'name' property.
111 std::string command_name;
Alex Vakulenkof784e212015-04-20 12:33:52 -0700112 if (!json->GetString(commands::attributes::kCommand_Name, &command_name)) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800113 chromeos::Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700114 errors::commands::kPropertyMissing,
115 "Command name is missing");
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700116 return instance;
117 }
118 // Make sure we know how to handle the command with this name.
Anton Muhincfde8692014-11-25 03:36:59 +0400119 auto command_def = dictionary.FindCommand(command_name);
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700120 if (!command_def) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800121 chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700122 errors::commands::kInvalidCommandName,
123 "Unknown command received: %s",
124 command_name.c_str());
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700125 return instance;
126 }
127
128 native_types::Object parameters;
Alex Vakulenko5ef75792015-03-19 15:50:44 -0700129 if (!GetCommandParameters(json, command_def, &parameters, error)) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800130 chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700131 errors::commands::kCommandFailed,
132 "Failed to validate command '%s'",
133 command_name.c_str());
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700134 return instance;
135 }
136
Alex Vakulenkof784e212015-04-20 12:33:52 -0700137 instance.reset(
138 new CommandInstance{command_name, origin, command_def, parameters});
Anton Muhin5191e812014-10-30 17:49:48 +0400139 std::string command_id;
Alex Vakulenkof784e212015-04-20 12:33:52 -0700140 if (json->GetString(commands::attributes::kCommand_Id, &command_id)) {
Anton Muhin5191e812014-10-30 17:49:48 +0400141 instance->SetID(command_id);
142 }
143
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700144 return instance;
145}
146
Vitaly Buka906d39e2015-03-24 10:08:26 -0700147std::unique_ptr<base::DictionaryValue> CommandInstance::ToJson() const {
148 std::unique_ptr<base::DictionaryValue> json{new base::DictionaryValue};
149
150 json->SetString(commands::attributes::kCommand_Id, id_);
151 json->SetString(commands::attributes::kCommand_Name, name_);
152 json->Set(commands::attributes::kCommand_Parameters,
153 TypedValueToJson(parameters_, nullptr).release());
154 json->Set(commands::attributes::kCommand_Results,
155 TypedValueToJson(results_, nullptr).release());
Alex Vakulenko808e2d82015-04-08 15:45:56 -0700156 json->Set(commands::attributes::kCommand_Progress,
157 GetProgressJson().release());
Vitaly Buka906d39e2015-03-24 10:08:26 -0700158 json->SetString(commands::attributes::kCommand_State, status_);
159
160 return json;
161}
162
Anton Muhinb66a9302014-11-10 22:15:22 +0400163void CommandInstance::AddProxy(std::unique_ptr<CommandProxyInterface> proxy) {
164 proxies_.push_back(std::move(proxy));
165}
166
Anton Muhincfde8692014-11-25 03:36:59 +0400167bool CommandInstance::SetResults(const native_types::Object& results) {
168 // TODO(antonm): Add validation.
169 if (results != results_) {
170 results_ = results;
171 for (auto& proxy : proxies_) {
Alex Vakulenkob211c102015-04-21 11:43:23 -0700172 proxy->OnResultsChanged();
Anton Muhincfde8692014-11-25 03:36:59 +0400173 }
174 }
175 return true;
176}
177
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700178bool CommandInstance::SetProgress(int progress) {
179 if (progress < 0 || progress > 100)
180 return false;
181 if (progress != progress_) {
182 progress_ = progress;
183 SetStatus(kStatusInProgress);
Anton Muhin4dc37852014-10-30 22:17:25 +0400184 for (auto& proxy : proxies_) {
Alex Vakulenkob211c102015-04-21 11:43:23 -0700185 proxy->OnProgressChanged();
Anton Muhin4dc37852014-10-30 22:17:25 +0400186 }
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700187 }
188 return true;
189}
190
191void CommandInstance::Abort() {
192 SetStatus(kStatusAborted);
193 RemoveFromQueue();
194 // The command will be destroyed after that, so do not access any members.
195}
196
197void CommandInstance::Cancel() {
Alex Vakulenkodb221242015-03-13 14:02:46 -0700198 SetStatus(kStatusCancelled);
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700199 RemoveFromQueue();
200 // The command will be destroyed after that, so do not access any members.
201}
202
203void CommandInstance::Done() {
204 SetProgress(100);
205 SetStatus(kStatusDone);
206 RemoveFromQueue();
207 // The command will be destroyed after that, so do not access any members.
208}
209
210void CommandInstance::SetStatus(const std::string& status) {
211 if (status != status_) {
212 status_ = status;
Anton Muhin4dc37852014-10-30 22:17:25 +0400213 for (auto& proxy : proxies_) {
Alex Vakulenkob211c102015-04-21 11:43:23 -0700214 proxy->OnStatusChanged();
Anton Muhin4dc37852014-10-30 22:17:25 +0400215 }
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700216 }
217}
218
219void CommandInstance::RemoveFromQueue() {
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700220 if (queue_)
221 queue_->DelayedRemove(GetID());
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700222}
223
Alex Vakulenko808e2d82015-04-08 15:45:56 -0700224std::unique_ptr<base::Value> CommandInstance::GetProgressJson() const {
225 // GCD server requires "progress" to be a JSON object. We will just make
226 // an object with a single field, "progress", so the patch request will
227 // look like this: {"progress": {"progress":100}}.
228 std::unique_ptr<base::DictionaryValue> progress_object{
229 new base::DictionaryValue};
230 progress_object->SetInteger(commands::attributes::kCommand_Progress,
231 progress_);
232 return std::move(progress_object);
233}
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -0700234
235} // namespace buffet