|  | // Copyright 2014 The Chromium OS Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "buffet/commands/command_instance.h" | 
|  |  | 
|  | #include <base/values.h> | 
|  | #include <chromeos/errors/error.h> | 
|  | #include <chromeos/errors/error_codes.h> | 
|  |  | 
|  | #include "buffet/commands/command_definition.h" | 
|  | #include "buffet/commands/command_dictionary.h" | 
|  | #include "buffet/commands/command_proxy_interface.h" | 
|  | #include "buffet/commands/command_queue.h" | 
|  | #include "buffet/commands/schema_constants.h" | 
|  | #include "buffet/commands/schema_utils.h" | 
|  |  | 
|  | namespace buffet { | 
|  |  | 
|  | const char CommandInstance::kStatusQueued[] = "queued"; | 
|  | const char CommandInstance::kStatusInProgress[] = "inProgress"; | 
|  | const char CommandInstance::kStatusPaused[] = "paused"; | 
|  | const char CommandInstance::kStatusError[] = "error"; | 
|  | const char CommandInstance::kStatusDone[] = "done"; | 
|  | const char CommandInstance::kStatusCanceled[] = "canceled"; | 
|  | const char CommandInstance::kStatusAborted[] = "aborted"; | 
|  | const char CommandInstance::kStatusExpired[] = "expired"; | 
|  |  | 
|  | CommandInstance::CommandInstance(const std::string& name, | 
|  | const std::string& category, | 
|  | const native_types::Object& parameters) | 
|  | : name_(name), category_(category), parameters_(parameters) { | 
|  | } | 
|  |  | 
|  | std::shared_ptr<const PropValue> CommandInstance::FindParameter( | 
|  | const std::string& name) const { | 
|  | std::shared_ptr<const PropValue> value; | 
|  | auto p = parameters_.find(name); | 
|  | if (p != parameters_.end()) | 
|  | value = p->second; | 
|  | return value; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Helper method to retrieve command parameters from the command definition | 
|  | // object passed in as |json| and corresponding command definition schema | 
|  | // specified in |command_def|. | 
|  | // On success, returns |true| and the validated parameters and values through | 
|  | // |parameters|. Otherwise returns |false| and additional error information in | 
|  | // |error|. | 
|  | bool GetCommandParameters(const base::DictionaryValue* json, | 
|  | const CommandDefinition* command_def, | 
|  | native_types::Object* parameters, | 
|  | chromeos::ErrorPtr* error) { | 
|  | // Get the command parameters from 'parameters' property. | 
|  | 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)) { | 
|  | // Make sure the "parameters" property is actually an object. | 
|  | if (!params_value->GetAsDictionary(¶ms)) { | 
|  | chromeos::Error::AddToPrintf(error, chromeos::errors::json::kDomain, | 
|  | chromeos::errors::json::kObjectExpected, | 
|  | "Property '%s' must be a JSON object", | 
|  | commands::attributes::kCommand_Parameters); | 
|  | return false; | 
|  | } | 
|  | } else { | 
|  | // "parameters" are not specified. Assume empty param list. | 
|  | params = &no_params; | 
|  | } | 
|  |  | 
|  | // Now read in the parameters and validate their values against the command | 
|  | // definition schema. | 
|  | if (!TypedValueFromJson(params, command_def->GetParameters().get(), | 
|  | parameters, error)) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | std::unique_ptr<CommandInstance> CommandInstance::FromJson( | 
|  | const base::Value* value, | 
|  | const CommandDictionary& dictionary, | 
|  | chromeos::ErrorPtr* error) { | 
|  | std::unique_ptr<CommandInstance> instance; | 
|  | // Get the command JSON object from the value. | 
|  | const base::DictionaryValue* json = nullptr; | 
|  | if (!value->GetAsDictionary(&json)) { | 
|  | chromeos::Error::AddTo(error, chromeos::errors::json::kDomain, | 
|  | chromeos::errors::json::kObjectExpected, | 
|  | "Command instance is not a JSON object"); | 
|  | return instance; | 
|  | } | 
|  |  | 
|  | // Get the command name from 'name' property. | 
|  | std::string command_name; | 
|  | if (!json->GetStringWithoutPathExpansion(commands::attributes::kCommand_Name, | 
|  | &command_name)) { | 
|  | chromeos::Error::AddTo(error, errors::commands::kDomain, | 
|  | errors::commands::kPropertyMissing, | 
|  | "Command name is missing"); | 
|  | return instance; | 
|  | } | 
|  | // Make sure we know how to handle the command with this name. | 
|  | const CommandDefinition* command_def = dictionary.FindCommand(command_name); | 
|  | if (!command_def) { | 
|  | chromeos::Error::AddToPrintf(error, errors::commands::kDomain, | 
|  | errors::commands::kInvalidCommandName, | 
|  | "Unknown command received: %s", | 
|  | command_name.c_str()); | 
|  | return instance; | 
|  | } | 
|  |  | 
|  | native_types::Object parameters; | 
|  | if (!GetCommandParameters(json, command_def, ¶meters, error)) { | 
|  | chromeos::Error::AddToPrintf(error, errors::commands::kDomain, | 
|  | errors::commands::kCommandFailed, | 
|  | "Failed to validate command '%s'", | 
|  | command_name.c_str()); | 
|  | return instance; | 
|  | } | 
|  |  | 
|  | instance.reset(new CommandInstance(command_name, | 
|  | command_def->GetCategory(), | 
|  | parameters)); | 
|  | // TODO(antonm): Move command_id to ctor and remove setter. | 
|  | std::string command_id; | 
|  | if (json->GetStringWithoutPathExpansion(commands::attributes::kCommand_Id, | 
|  | &command_id)) { | 
|  | instance->SetID(command_id); | 
|  | } | 
|  |  | 
|  | return instance; | 
|  | } | 
|  |  | 
|  | bool CommandInstance::SetProgress(int progress) { | 
|  | if (progress < 0 || progress > 100) | 
|  | return false; | 
|  | if (progress != progress_) { | 
|  | progress_ = progress; | 
|  | SetStatus(kStatusInProgress); | 
|  | for (auto& proxy : proxies_) { | 
|  | proxy->OnProgressChanged(progress_); | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void CommandInstance::Abort() { | 
|  | SetStatus(kStatusAborted); | 
|  | RemoveFromQueue(); | 
|  | // The command will be destroyed after that, so do not access any members. | 
|  | } | 
|  |  | 
|  | void CommandInstance::Cancel() { | 
|  | SetStatus(kStatusCanceled); | 
|  | RemoveFromQueue(); | 
|  | // The command will be destroyed after that, so do not access any members. | 
|  | } | 
|  |  | 
|  | void CommandInstance::Done() { | 
|  | SetProgress(100); | 
|  | SetStatus(kStatusDone); | 
|  | RemoveFromQueue(); | 
|  | // The command will be destroyed after that, so do not access any members. | 
|  | } | 
|  |  | 
|  | void CommandInstance::SetStatus(const std::string& status) { | 
|  | if (status != status_) { | 
|  | status_ = status; | 
|  | for (auto& proxy : proxies_) { | 
|  | proxy->OnStatusChanged(status_); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void CommandInstance::RemoveFromQueue() { | 
|  | if (queue_) { | 
|  | // Store this instance in unique_ptr until the end of this method, | 
|  | // otherwise it will be destroyed as soon as CommandQueue::Remove() returns. | 
|  | std::unique_ptr<CommandInstance> this_instance = queue_->Remove(GetID()); | 
|  | // The command instance will survive till the end of this scope. | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | }  // namespace buffet |