blob: 926dcf55f036aa3c3ea99177a12e49d7298a0731 [file] [log] [blame]
Alex Vakulenko3cc95aa2014-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 Vakulenkoc2abd662014-08-07 10:29:42 -07007#include <base/values.h>
Alex Vakulenko24e5f5d2014-08-27 11:00:57 -07008#include <chromeos/errors/error.h>
9#include <chromeos/errors/error_codes.h>
Alex Vakulenkoc2abd662014-08-07 10:29:42 -070010
11#include "buffet/commands/command_definition.h"
12#include "buffet/commands/command_dictionary.h"
Alex Vakulenko9158b442014-09-03 16:23:38 -070013#include "buffet/commands/command_proxy_interface.h"
14#include "buffet/commands/command_queue.h"
Alex Vakulenkoc1459e62015-03-18 09:54:37 -070015#include "buffet/commands/prop_types.h"
Alex Vakulenkoc2abd662014-08-07 10:29:42 -070016#include "buffet/commands/schema_constants.h"
17#include "buffet/commands/schema_utils.h"
Alex Vakulenkoc2abd662014-08-07 10:29:42 -070018
Alex Vakulenko3cc95aa2014-08-07 07:24:06 -070019namespace buffet {
20
Alex Vakulenko9158b442014-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 Vakulenko71beab32015-03-13 14:02:46 -070026const char CommandInstance::kStatusCancelled[] = "cancelled";
Alex Vakulenko9158b442014-09-03 16:23:38 -070027const char CommandInstance::kStatusAborted[] = "aborted";
28const char CommandInstance::kStatusExpired[] = "expired";
29
Alex Vakulenko599ec762015-03-19 15:50:44 -070030CommandInstance::CommandInstance(const std::string& name,
31 const CommandDefinition* command_definition,
32 const native_types::Object& parameters)
33 : name_{name},
34 command_definition_{command_definition},
35 parameters_{parameters} {
36 CHECK(command_definition_);
Alex Vakulenko3cc95aa2014-08-07 07:24:06 -070037}
38
Anton Muhin182fad32014-11-10 22:15:22 +040039CommandInstance::~CommandInstance() = default;
40
Anton Muhin9ac45862014-11-25 03:36:59 +040041const std::string& CommandInstance::GetCategory() const {
42 return command_definition_->GetCategory();
43}
44
Alex Vakulenko599ec762015-03-19 15:50:44 -070045const PropValue* CommandInstance::FindParameter(const std::string& name) const {
Alex Vakulenko3cc95aa2014-08-07 07:24:06 -070046 auto p = parameters_.find(name);
Alex Vakulenko599ec762015-03-19 15:50:44 -070047 return (p != parameters_.end()) ? p->second.get() : nullptr;
Alex Vakulenko3cc95aa2014-08-07 07:24:06 -070048}
49
Alex Vakulenkoc2abd662014-08-07 10:29:42 -070050namespace {
51
52// Helper method to retrieve command parameters from the command definition
53// object passed in as |json| and corresponding command definition schema
54// specified in |command_def|.
55// On success, returns |true| and the validated parameters and values through
56// |parameters|. Otherwise returns |false| and additional error information in
57// |error|.
58bool GetCommandParameters(const base::DictionaryValue* json,
59 const CommandDefinition* command_def,
60 native_types::Object* parameters,
Alex Vakulenkofea44e92014-08-14 17:54:04 -070061 chromeos::ErrorPtr* error) {
Alex Vakulenkoc2abd662014-08-07 10:29:42 -070062 // Get the command parameters from 'parameters' property.
63 base::DictionaryValue no_params; // Placeholder when no params are specified.
64 const base::DictionaryValue* params = nullptr;
65 const base::Value* params_value = nullptr;
66 if (json->GetWithoutPathExpansion(commands::attributes::kCommand_Parameters,
67 &params_value)) {
68 // Make sure the "parameters" property is actually an object.
69 if (!params_value->GetAsDictionary(&params)) {
Alex Vakulenko90b3da92014-11-11 11:42:05 -080070 chromeos::Error::AddToPrintf(error, FROM_HERE,
71 chromeos::errors::json::kDomain,
Alex Vakulenkofea44e92014-08-14 17:54:04 -070072 chromeos::errors::json::kObjectExpected,
73 "Property '%s' must be a JSON object",
74 commands::attributes::kCommand_Parameters);
Alex Vakulenkoc2abd662014-08-07 10:29:42 -070075 return false;
76 }
77 } else {
78 // "parameters" are not specified. Assume empty param list.
79 params = &no_params;
80 }
81
82 // Now read in the parameters and validate their values against the command
83 // definition schema.
Alex Vakulenkoc1459e62015-03-18 09:54:37 -070084 ObjectPropType obj_prop_type;
Alex Vakulenko599ec762015-03-19 15:50:44 -070085 obj_prop_type.SetObjectSchema(command_def->GetParameters()->Clone());
Alex Vakulenkoc1459e62015-03-18 09:54:37 -070086 if (!TypedValueFromJson(params, &obj_prop_type, parameters, error)) {
Alex Vakulenkoc2abd662014-08-07 10:29:42 -070087 return false;
88 }
89 return true;
90}
91
92} // anonymous namespace
93
Alex Vakulenko6c5a6022014-08-20 12:38:43 -070094std::unique_ptr<CommandInstance> CommandInstance::FromJson(
Alex Vakulenkoc2abd662014-08-07 10:29:42 -070095 const base::Value* value,
96 const CommandDictionary& dictionary,
Alex Vakulenkofea44e92014-08-14 17:54:04 -070097 chromeos::ErrorPtr* error) {
Alex Vakulenko6c5a6022014-08-20 12:38:43 -070098 std::unique_ptr<CommandInstance> instance;
Alex Vakulenkoc2abd662014-08-07 10:29:42 -070099 // Get the command JSON object from the value.
100 const base::DictionaryValue* json = nullptr;
101 if (!value->GetAsDictionary(&json)) {
Alex Vakulenko90b3da92014-11-11 11:42:05 -0800102 chromeos::Error::AddTo(error, FROM_HERE, chromeos::errors::json::kDomain,
Alex Vakulenkofea44e92014-08-14 17:54:04 -0700103 chromeos::errors::json::kObjectExpected,
104 "Command instance is not a JSON object");
Alex Vakulenkoc2abd662014-08-07 10:29:42 -0700105 return instance;
106 }
107
108 // Get the command name from 'name' property.
109 std::string command_name;
110 if (!json->GetStringWithoutPathExpansion(commands::attributes::kCommand_Name,
111 &command_name)) {
Alex Vakulenko90b3da92014-11-11 11:42:05 -0800112 chromeos::Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenkofea44e92014-08-14 17:54:04 -0700113 errors::commands::kPropertyMissing,
114 "Command name is missing");
Alex Vakulenkoc2abd662014-08-07 10:29:42 -0700115 return instance;
116 }
117 // Make sure we know how to handle the command with this name.
Anton Muhin9ac45862014-11-25 03:36:59 +0400118 auto command_def = dictionary.FindCommand(command_name);
Alex Vakulenkoc2abd662014-08-07 10:29:42 -0700119 if (!command_def) {
Alex Vakulenko90b3da92014-11-11 11:42:05 -0800120 chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenkofea44e92014-08-14 17:54:04 -0700121 errors::commands::kInvalidCommandName,
122 "Unknown command received: %s",
123 command_name.c_str());
Alex Vakulenkoc2abd662014-08-07 10:29:42 -0700124 return instance;
125 }
126
127 native_types::Object parameters;
Alex Vakulenko599ec762015-03-19 15:50:44 -0700128 if (!GetCommandParameters(json, command_def, &parameters, error)) {
Alex Vakulenko90b3da92014-11-11 11:42:05 -0800129 chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenkofea44e92014-08-14 17:54:04 -0700130 errors::commands::kCommandFailed,
131 "Failed to validate command '%s'",
132 command_name.c_str());
Alex Vakulenkoc2abd662014-08-07 10:29:42 -0700133 return instance;
134 }
135
Anton Muhin9ac45862014-11-25 03:36:59 +0400136 instance.reset(new CommandInstance(command_name, command_def, parameters));
Anton Muhind014d8b2014-10-30 17:49:48 +0400137 std::string command_id;
138 if (json->GetStringWithoutPathExpansion(commands::attributes::kCommand_Id,
139 &command_id)) {
140 instance->SetID(command_id);
141 }
142
Alex Vakulenkoc2abd662014-08-07 10:29:42 -0700143 return instance;
144}
145
Vitaly Buka02afd032015-03-24 10:08:26 -0700146std::unique_ptr<base::DictionaryValue> CommandInstance::ToJson() const {
147 std::unique_ptr<base::DictionaryValue> json{new base::DictionaryValue};
148
149 json->SetString(commands::attributes::kCommand_Id, id_);
150 json->SetString(commands::attributes::kCommand_Name, name_);
151 json->Set(commands::attributes::kCommand_Parameters,
152 TypedValueToJson(parameters_, nullptr).release());
153 json->Set(commands::attributes::kCommand_Results,
154 TypedValueToJson(results_, nullptr).release());
155 json->SetInteger(commands::attributes::kCommand_Progress, progress_);
156 json->SetString(commands::attributes::kCommand_State, status_);
157
158 return json;
159}
160
Anton Muhin182fad32014-11-10 22:15:22 +0400161void CommandInstance::AddProxy(std::unique_ptr<CommandProxyInterface> proxy) {
162 proxies_.push_back(std::move(proxy));
163}
164
Anton Muhin9ac45862014-11-25 03:36:59 +0400165bool CommandInstance::SetResults(const native_types::Object& results) {
166 // TODO(antonm): Add validation.
167 if (results != results_) {
168 results_ = results;
169 for (auto& proxy : proxies_) {
170 proxy->OnResultsChanged(results_);
171 }
172 }
173 return true;
174}
175
Alex Vakulenko9158b442014-09-03 16:23:38 -0700176bool CommandInstance::SetProgress(int progress) {
177 if (progress < 0 || progress > 100)
178 return false;
179 if (progress != progress_) {
180 progress_ = progress;
181 SetStatus(kStatusInProgress);
Anton Muhin5beea3c2014-10-30 22:17:25 +0400182 for (auto& proxy : proxies_) {
183 proxy->OnProgressChanged(progress_);
184 }
Alex Vakulenko9158b442014-09-03 16:23:38 -0700185 }
186 return true;
187}
188
189void CommandInstance::Abort() {
190 SetStatus(kStatusAborted);
191 RemoveFromQueue();
192 // The command will be destroyed after that, so do not access any members.
193}
194
195void CommandInstance::Cancel() {
Alex Vakulenko71beab32015-03-13 14:02:46 -0700196 SetStatus(kStatusCancelled);
Alex Vakulenko9158b442014-09-03 16:23:38 -0700197 RemoveFromQueue();
198 // The command will be destroyed after that, so do not access any members.
199}
200
201void CommandInstance::Done() {
202 SetProgress(100);
203 SetStatus(kStatusDone);
204 RemoveFromQueue();
205 // The command will be destroyed after that, so do not access any members.
206}
207
208void CommandInstance::SetStatus(const std::string& status) {
209 if (status != status_) {
210 status_ = status;
Anton Muhin5beea3c2014-10-30 22:17:25 +0400211 for (auto& proxy : proxies_) {
212 proxy->OnStatusChanged(status_);
213 }
Alex Vakulenko9158b442014-09-03 16:23:38 -0700214 }
215}
216
217void CommandInstance::RemoveFromQueue() {
218 if (queue_) {
219 // Store this instance in unique_ptr until the end of this method,
220 // otherwise it will be destroyed as soon as CommandQueue::Remove() returns.
221 std::unique_ptr<CommandInstance> this_instance = queue_->Remove(GetID());
222 // The command instance will survive till the end of this scope.
223 }
224}
225
Alex Vakulenko3cc95aa2014-08-07 07:24:06 -0700226
227} // namespace buffet