blob: f6a409dc439892c68baa4326162f3d7e9a31adbe [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 <algorithm>
#include <base/values.h>
#include <weave/enum_to_string.h>
#include "src/commands/schema_constants.h"
#include "src/string_utils.h"
namespace weave {
namespace {
const EnumToStringMap<UserRole>::Map kMap[] = {
{UserRole::kViewer, commands::attributes::kCommand_Role_Viewer},
{UserRole::kUser, commands::attributes::kCommand_Role_User},
{UserRole::kOwner, commands::attributes::kCommand_Role_Owner},
{UserRole::kManager, commands::attributes::kCommand_Role_Manager},
};
} // anonymous namespace
template <>
LIBWEAVE_EXPORT EnumToStringMap<UserRole>::EnumToStringMap()
: EnumToStringMap(kMap) {}
bool CommandDictionary::LoadCommands(const base::DictionaryValue& json,
ErrorPtr* error) {
// |json| contains a list of nested objects with the following structure:
// {"<pkg_name>": {"<cmd_name>": {"parameters": {object_schema}}, ...}, ...}
// Iterate over traits
base::DictionaryValue::Iterator trait_iter(json);
for (base::DictionaryValue::Iterator trait_iter(json);
!trait_iter.IsAtEnd(); trait_iter.Advance()) {
std::string trait_name = trait_iter.key();
const base::DictionaryValue* trait_def = nullptr;
if (!trait_iter.value().GetAsDictionary(&trait_def)) {
Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kTypeMismatch,
"Expecting an object for trait '%s'",
trait_name.c_str());
return false;
}
// Iterate over command definitions within the current trait.
for (base::DictionaryValue::Iterator command_iter(*trait_def);
!command_iter.IsAtEnd(); command_iter.Advance()) {
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 trait '%s'",
trait_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 "trait_name.cmd_name".
std::string full_command_name = Join(".", trait_name, command_name);
// Validate the 'minimalRole' value if present. That's the only thing we
// care about so far.
std::string value;
if (!command_def_json->GetString(commands::attributes::kCommand_Role,
&value)) {
Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kInvalidMinimalRole,
"Missing '%s' attribute for command '%s'",
commands::attributes::kCommand_Role,
full_command_name.c_str());
return false;
}
UserRole minimal_role;
if (!StringToEnum(value, &minimal_role)) {
Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kInvalidMinimalRole,
"Invalid role '%s' for command '%s'", value.c_str(),
full_command_name.c_str());
return false;
}
// Check if we already have this command defined.
CHECK(!definitions_.Get(full_command_name, nullptr))
<< "Definition for command '" << full_command_name
<< "' overrides an earlier definition";
definitions_.Set(full_command_name, command_def_json->DeepCopy());
}
}
return true;
}
const base::DictionaryValue& CommandDictionary::GetCommandsAsJson() const {
return definitions_;
}
size_t CommandDictionary::GetSize() const {
size_t size = 0;
base::DictionaryValue::Iterator trait_iter(definitions_);
while (!trait_iter.IsAtEnd()) {
std::string trait_name = trait_iter.key();
const base::DictionaryValue* trait_def = nullptr;
CHECK(trait_iter.value().GetAsDictionary(&trait_def));
size += trait_def->size();
trait_iter.Advance();
}
return size;
}
const base::DictionaryValue* CommandDictionary::FindCommand(
const std::string& command_name) const {
const base::DictionaryValue* definition = nullptr;
// Make sure the |command_name| came in form of trait_name.command_name.
// For this, we just verify it has a single period in its name.
if (std::count(command_name.begin(), command_name.end(), '.') != 1)
return definition;
definitions_.GetDictionary(command_name, &definition);
return definition;
}
void CommandDictionary::Clear() {
definitions_.Clear();
}
bool CommandDictionary::GetMinimalRole(const std::string& command_name,
UserRole* minimal_role,
ErrorPtr* error) const {
const base::DictionaryValue* command_def = FindCommand(command_name);
if (!command_def) {
Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
errors::commands::kInvalidCommandName,
"Command definition for '%s' not found",
command_name.c_str());
return false;
}
std::string value;
// The JSON definition has been pre-validated already in LoadCommands, so
// just using CHECKs here.
CHECK(command_def->GetString(commands::attributes::kCommand_Role, &value));
CHECK(StringToEnum(value, minimal_role));
return true;
}
} // namespace weave