blob: a2e098f92e5f85d650f73d3fffc53ba943872c95 [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 Vakulenkod1978d32015-04-29 17:33:26 -070099 std::string* command_id,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700100 chromeos::ErrorPtr* error) {
Alex Vakulenkofedc4872014-08-20 12:38:43 -0700101 std::unique_ptr<CommandInstance> instance;
Alex Vakulenkod1978d32015-04-29 17:33:26 -0700102 std::string command_id_buffer; // used if |command_id| was nullptr.
103 if (!command_id)
104 command_id = &command_id_buffer;
105
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700106 // Get the command JSON object from the value.
107 const base::DictionaryValue* json = nullptr;
108 if (!value->GetAsDictionary(&json)) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800109 chromeos::Error::AddTo(error, FROM_HERE, chromeos::errors::json::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700110 chromeos::errors::json::kObjectExpected,
111 "Command instance is not a JSON object");
Alex Vakulenkod1978d32015-04-29 17:33:26 -0700112 command_id->clear();
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700113 return instance;
114 }
115
Alex Vakulenkod1978d32015-04-29 17:33:26 -0700116 // Get the command ID from 'id' property.
117 if (!json->GetString(commands::attributes::kCommand_Id, command_id))
118 command_id->clear();
119
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700120 // Get the command name from 'name' property.
121 std::string command_name;
Alex Vakulenkof784e212015-04-20 12:33:52 -0700122 if (!json->GetString(commands::attributes::kCommand_Name, &command_name)) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800123 chromeos::Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700124 errors::commands::kPropertyMissing,
125 "Command name is missing");
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700126 return instance;
127 }
128 // Make sure we know how to handle the command with this name.
Anton Muhincfde8692014-11-25 03:36:59 +0400129 auto command_def = dictionary.FindCommand(command_name);
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700130 if (!command_def) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800131 chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700132 errors::commands::kInvalidCommandName,
133 "Unknown command received: %s",
134 command_name.c_str());
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700135 return instance;
136 }
137
138 native_types::Object parameters;
Alex Vakulenko5ef75792015-03-19 15:50:44 -0700139 if (!GetCommandParameters(json, command_def, &parameters, error)) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800140 chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700141 errors::commands::kCommandFailed,
142 "Failed to validate command '%s'",
143 command_name.c_str());
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700144 return instance;
145 }
146
Alex Vakulenkof784e212015-04-20 12:33:52 -0700147 instance.reset(
148 new CommandInstance{command_name, origin, command_def, parameters});
Alex Vakulenkod1978d32015-04-29 17:33:26 -0700149
150 if (!command_id->empty())
151 instance->SetID(*command_id);
Anton Muhin5191e812014-10-30 17:49:48 +0400152
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700153 return instance;
154}
155
Vitaly Buka906d39e2015-03-24 10:08:26 -0700156std::unique_ptr<base::DictionaryValue> CommandInstance::ToJson() const {
157 std::unique_ptr<base::DictionaryValue> json{new base::DictionaryValue};
158
159 json->SetString(commands::attributes::kCommand_Id, id_);
160 json->SetString(commands::attributes::kCommand_Name, name_);
161 json->Set(commands::attributes::kCommand_Parameters,
162 TypedValueToJson(parameters_, nullptr).release());
Vitaly Buka4129dfa2015-04-29 12:16:58 -0700163 json->Set(commands::attributes::kCommand_Progress,
164 TypedValueToJson(progress_, nullptr).release());
Vitaly Buka906d39e2015-03-24 10:08:26 -0700165 json->Set(commands::attributes::kCommand_Results,
166 TypedValueToJson(results_, nullptr).release());
Vitaly Buka906d39e2015-03-24 10:08:26 -0700167 json->SetString(commands::attributes::kCommand_State, status_);
168
169 return json;
170}
171
Anton Muhinb66a9302014-11-10 22:15:22 +0400172void CommandInstance::AddProxy(std::unique_ptr<CommandProxyInterface> proxy) {
173 proxies_.push_back(std::move(proxy));
174}
175
Anton Muhincfde8692014-11-25 03:36:59 +0400176bool CommandInstance::SetResults(const native_types::Object& results) {
177 // TODO(antonm): Add validation.
178 if (results != results_) {
179 results_ = results;
180 for (auto& proxy : proxies_) {
Alex Vakulenkob211c102015-04-21 11:43:23 -0700181 proxy->OnResultsChanged();
Anton Muhincfde8692014-11-25 03:36:59 +0400182 }
183 }
184 return true;
185}
186
Vitaly Buka4129dfa2015-04-29 12:16:58 -0700187bool CommandInstance::SetProgress(const native_types::Object& progress) {
188 // Change status even if progress unchanged, e.g. 0% -> 0%.
189 SetStatus(kStatusInProgress);
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700190 if (progress != progress_) {
191 progress_ = progress;
Anton Muhin4dc37852014-10-30 22:17:25 +0400192 for (auto& proxy : proxies_) {
Alex Vakulenkob211c102015-04-21 11:43:23 -0700193 proxy->OnProgressChanged();
Anton Muhin4dc37852014-10-30 22:17:25 +0400194 }
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700195 }
196 return true;
197}
198
199void CommandInstance::Abort() {
200 SetStatus(kStatusAborted);
201 RemoveFromQueue();
202 // The command will be destroyed after that, so do not access any members.
203}
204
205void CommandInstance::Cancel() {
Alex Vakulenkodb221242015-03-13 14:02:46 -0700206 SetStatus(kStatusCancelled);
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700207 RemoveFromQueue();
208 // The command will be destroyed after that, so do not access any members.
209}
210
211void CommandInstance::Done() {
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700212 SetStatus(kStatusDone);
213 RemoveFromQueue();
214 // The command will be destroyed after that, so do not access any members.
215}
216
217void CommandInstance::SetStatus(const std::string& status) {
218 if (status != status_) {
219 status_ = status;
Anton Muhin4dc37852014-10-30 22:17:25 +0400220 for (auto& proxy : proxies_) {
Alex Vakulenkob211c102015-04-21 11:43:23 -0700221 proxy->OnStatusChanged();
Anton Muhin4dc37852014-10-30 22:17:25 +0400222 }
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700223 }
224}
225
226void CommandInstance::RemoveFromQueue() {
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700227 if (queue_)
228 queue_->DelayedRemove(GetID());
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700229}
230
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -0700231} // namespace buffet