blob: 7d54ed79842e19eabdbf22fa3c34ae6cf79eea81 [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 Vakulenko8dc69af2014-08-07 10:29:42 -070015#include "buffet/commands/schema_constants.h"
16#include "buffet/commands/schema_utils.h"
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070017
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070018namespace buffet {
19
Alex Vakulenkof6b38712014-09-03 16:23:38 -070020const char CommandInstance::kStatusQueued[] = "queued";
21const char CommandInstance::kStatusInProgress[] = "inProgress";
22const char CommandInstance::kStatusPaused[] = "paused";
23const char CommandInstance::kStatusError[] = "error";
24const char CommandInstance::kStatusDone[] = "done";
25const char CommandInstance::kStatusCanceled[] = "canceled";
26const char CommandInstance::kStatusAborted[] = "aborted";
27const char CommandInstance::kStatusExpired[] = "expired";
28
Anton Muhincfde8692014-11-25 03:36:59 +040029CommandInstance::CommandInstance(
30 const std::string& name,
31 const std::shared_ptr<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_.get());
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070037}
38
Anton Muhinb66a9302014-11-10 22:15:22 +040039CommandInstance::~CommandInstance() = default;
40
Anton Muhincfde8692014-11-25 03:36:59 +040041const std::string& CommandInstance::GetCategory() const {
42 return command_definition_->GetCategory();
43}
44
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070045std::shared_ptr<const PropValue> CommandInstance::FindParameter(
46 const std::string& name) const {
47 std::shared_ptr<const PropValue> value;
48 auto p = parameters_.find(name);
49 if (p != parameters_.end())
50 value = p->second;
51 return value;
52}
53
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070054namespace {
55
56// Helper method to retrieve command parameters from the command definition
57// object passed in as |json| and corresponding command definition schema
58// specified in |command_def|.
59// On success, returns |true| and the validated parameters and values through
60// |parameters|. Otherwise returns |false| and additional error information in
61// |error|.
62bool GetCommandParameters(const base::DictionaryValue* json,
63 const CommandDefinition* command_def,
64 native_types::Object* parameters,
Alex Vakulenko5f472062014-08-14 17:54:04 -070065 chromeos::ErrorPtr* error) {
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070066 // Get the command parameters from 'parameters' property.
67 base::DictionaryValue no_params; // Placeholder when no params are specified.
68 const base::DictionaryValue* params = nullptr;
69 const base::Value* params_value = nullptr;
70 if (json->GetWithoutPathExpansion(commands::attributes::kCommand_Parameters,
71 &params_value)) {
72 // Make sure the "parameters" property is actually an object.
73 if (!params_value->GetAsDictionary(&params)) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -080074 chromeos::Error::AddToPrintf(error, FROM_HERE,
75 chromeos::errors::json::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -070076 chromeos::errors::json::kObjectExpected,
77 "Property '%s' must be a JSON object",
78 commands::attributes::kCommand_Parameters);
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070079 return false;
80 }
81 } else {
82 // "parameters" are not specified. Assume empty param list.
83 params = &no_params;
84 }
85
86 // Now read in the parameters and validate their values against the command
87 // definition schema.
88 if (!TypedValueFromJson(params, command_def->GetParameters().get(),
89 parameters, error)) {
90 return false;
91 }
92 return true;
93}
94
95} // anonymous namespace
96
Alex Vakulenkofedc4872014-08-20 12:38:43 -070097std::unique_ptr<CommandInstance> CommandInstance::FromJson(
Alex Vakulenko8dc69af2014-08-07 10:29:42 -070098 const base::Value* value,
99 const CommandDictionary& dictionary,
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 Vakulenko8dc69af2014-08-07 10:29:42 -0700102 // Get the command JSON object from the value.
103 const base::DictionaryValue* json = nullptr;
104 if (!value->GetAsDictionary(&json)) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800105 chromeos::Error::AddTo(error, FROM_HERE, chromeos::errors::json::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700106 chromeos::errors::json::kObjectExpected,
107 "Command instance is not a JSON object");
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700108 return instance;
109 }
110
111 // Get the command name from 'name' property.
112 std::string command_name;
113 if (!json->GetStringWithoutPathExpansion(commands::attributes::kCommand_Name,
114 &command_name)) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800115 chromeos::Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700116 errors::commands::kPropertyMissing,
117 "Command name is missing");
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700118 return instance;
119 }
120 // Make sure we know how to handle the command with this name.
Anton Muhincfde8692014-11-25 03:36:59 +0400121 auto command_def = dictionary.FindCommand(command_name);
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700122 if (!command_def) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800123 chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700124 errors::commands::kInvalidCommandName,
125 "Unknown command received: %s",
126 command_name.c_str());
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700127 return instance;
128 }
129
130 native_types::Object parameters;
Anton Muhincfde8692014-11-25 03:36:59 +0400131 if (!GetCommandParameters(json, command_def.get(), &parameters, error)) {
Alex Vakulenkoac8037d2014-11-11 11:42:05 -0800132 chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700133 errors::commands::kCommandFailed,
134 "Failed to validate command '%s'",
135 command_name.c_str());
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700136 return instance;
137 }
138
Anton Muhincfde8692014-11-25 03:36:59 +0400139 instance.reset(new CommandInstance(command_name, command_def, parameters));
Anton Muhin5191e812014-10-30 17:49:48 +0400140 // TODO(antonm): Move command_id to ctor and remove setter.
141 std::string command_id;
142 if (json->GetStringWithoutPathExpansion(commands::attributes::kCommand_Id,
143 &command_id)) {
144 instance->SetID(command_id);
145 }
146
Alex Vakulenko8dc69af2014-08-07 10:29:42 -0700147 return instance;
148}
149
Anton Muhinb66a9302014-11-10 22:15:22 +0400150void CommandInstance::AddProxy(std::unique_ptr<CommandProxyInterface> proxy) {
151 proxies_.push_back(std::move(proxy));
152}
153
Anton Muhincfde8692014-11-25 03:36:59 +0400154bool CommandInstance::SetResults(const native_types::Object& results) {
155 // TODO(antonm): Add validation.
156 if (results != results_) {
157 results_ = results;
158 for (auto& proxy : proxies_) {
159 proxy->OnResultsChanged(results_);
160 }
161 }
162 return true;
163}
164
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700165bool CommandInstance::SetProgress(int progress) {
166 if (progress < 0 || progress > 100)
167 return false;
168 if (progress != progress_) {
169 progress_ = progress;
170 SetStatus(kStatusInProgress);
Anton Muhin4dc37852014-10-30 22:17:25 +0400171 for (auto& proxy : proxies_) {
172 proxy->OnProgressChanged(progress_);
173 }
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700174 }
175 return true;
176}
177
178void CommandInstance::Abort() {
179 SetStatus(kStatusAborted);
180 RemoveFromQueue();
181 // The command will be destroyed after that, so do not access any members.
182}
183
184void CommandInstance::Cancel() {
185 SetStatus(kStatusCanceled);
186 RemoveFromQueue();
187 // The command will be destroyed after that, so do not access any members.
188}
189
190void CommandInstance::Done() {
191 SetProgress(100);
192 SetStatus(kStatusDone);
193 RemoveFromQueue();
194 // The command will be destroyed after that, so do not access any members.
195}
196
197void CommandInstance::SetStatus(const std::string& status) {
198 if (status != status_) {
199 status_ = status;
Anton Muhin4dc37852014-10-30 22:17:25 +0400200 for (auto& proxy : proxies_) {
201 proxy->OnStatusChanged(status_);
202 }
Alex Vakulenkof6b38712014-09-03 16:23:38 -0700203 }
204}
205
206void CommandInstance::RemoveFromQueue() {
207 if (queue_) {
208 // Store this instance in unique_ptr until the end of this method,
209 // otherwise it will be destroyed as soon as CommandQueue::Remove() returns.
210 std::unique_ptr<CommandInstance> this_instance = queue_->Remove(GetID());
211 // The command instance will survive till the end of this scope.
212 }
213}
214
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -0700215
216} // namespace buffet