blob: 516961f8fd0e3346423c5669639f05712d746b12 [file] [log] [blame]
// Copyright 2015 The Weave 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 "src/commands/command_dictionary.h"
#include <base/values.h>
#include <weave/enum_to_string.h>
#include "src/commands/command_definition.h"
#include "src/commands/schema_constants.h"
#include "src/string_utils.h"
namespace weave {
bool CommandDictionary::LoadCommands(const base::DictionaryValue& json,
ErrorPtr* error) {
CommandMap new_defs;
// |json| contains a list of nested objects with the following structure:
// {"<pkg_name>": {"<cmd_name>": {"parameters": {object_schema}}, ...}, ...}
// Iterate over packages
base::DictionaryValue::Iterator package_iter(json);
while (!package_iter.IsAtEnd()) {
std::string package_name = package_iter.key();
const base::DictionaryValue* package_value = nullptr;
if (!package_iter.value().GetAsDictionary(&package_value)) {
Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kTypeMismatch,
"Expecting an object for package '%s'",
package_name.c_str());
return false;
}
// Iterate over command definitions within the current package.
base::DictionaryValue::Iterator command_iter(*package_value);
while (!command_iter.IsAtEnd()) {
std::string command_name = command_iter.key();
if (command_name.empty()) {
Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kInvalidCommandName,
"Unnamed command encountered in package '%s'",
package_name.c_str());
return false;
}
const base::DictionaryValue* command_def_json = nullptr;
if (!command_iter.value().GetAsDictionary(&command_def_json)) {
Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kTypeMismatch,
"Expecting an object for command '%s'",
command_name.c_str());
return false;
}
// Construct the compound command name as "pkg_name.cmd_name".
std::string full_command_name = Join(".", package_name, command_name);
auto command_def = CommandDefinition::FromJson(*command_def_json, error);
if (!command_def) {
Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kInvalidMinimalRole,
"Error parsing command '%s'",
full_command_name.c_str());
return false;
}
new_defs.insert(
std::make_pair(full_command_name, std::move(command_def)));
command_iter.Advance();
}
package_iter.Advance();
}
// Verify that newly loaded command definitions do not override existing
// definitions in another category. This is unlikely, but we don't want to let
// one vendor daemon to define the same commands already handled by another
// daemon on the same device.
for (const auto& pair : new_defs) {
auto iter = definitions_.find(pair.first);
CHECK(iter == definitions_.end()) << "Definition for command '"
<< pair.first
<< "' overrides an earlier definition";
}
// Insert new definitions into the global map.
for (auto& pair : new_defs)
definitions_.insert(std::make_pair(pair.first, std::move(pair.second)));
return true;
}
std::unique_ptr<base::DictionaryValue> CommandDictionary::GetCommandsAsJson(
ErrorPtr* error) const {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
for (const auto& pair : definitions_) {
auto parts = SplitAtFirst(pair.first, ".", true);
const std::string& package_name = parts.first;
const std::string& command_name = parts.second;
base::DictionaryValue* package = nullptr;
if (!dict->GetDictionaryWithoutPathExpansion(package_name, &package)) {
// If this is the first time we encounter this package, create a JSON
// object for it.
package = new base::DictionaryValue;
dict->SetWithoutPathExpansion(package_name, package);
}
package->SetWithoutPathExpansion(command_name,
pair.second->ToJson().DeepCopy());
}
return dict;
}
const CommandDefinition* CommandDictionary::FindCommand(
const std::string& command_name) const {
auto pair = definitions_.find(command_name);
return (pair != definitions_.end()) ? pair->second.get() : nullptr;
}
CommandDefinition* CommandDictionary::FindCommand(
const std::string& command_name) {
auto pair = definitions_.find(command_name);
return (pair != definitions_.end()) ? pair->second.get() : nullptr;
}
void CommandDictionary::Clear() {
definitions_.clear();
}
} // namespace weave