Vitaly Buka | 4615e0d | 2015-10-14 15:35:12 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Weave Authors. All rights reserved. |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Stefan Sauer | 2d16dfa | 2015-09-25 17:08:35 +0200 | [diff] [blame] | 5 | #include "src/commands/command_dictionary.h" |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 6 | |
| 7 | #include <base/values.h> |
Vitaly Buka | 7b382ac | 2015-08-03 13:50:01 -0700 | [diff] [blame] | 8 | #include <weave/enum_to_string.h> |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 9 | |
Stefan Sauer | 2d16dfa | 2015-09-25 17:08:35 +0200 | [diff] [blame] | 10 | #include "src/commands/command_definition.h" |
| 11 | #include "src/commands/schema_constants.h" |
| 12 | #include "src/string_utils.h" |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 13 | |
Vitaly Buka | b6f015a | 2015-07-09 14:59:23 -0700 | [diff] [blame] | 14 | namespace weave { |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 15 | |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 16 | bool CommandDictionary::LoadCommands(const base::DictionaryValue& json, |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 17 | ErrorPtr* error) { |
Alex Vakulenko | 5ef7579 | 2015-03-19 15:50:44 -0700 | [diff] [blame] | 18 | CommandMap new_defs; |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 19 | |
| 20 | // |json| contains a list of nested objects with the following structure: |
| 21 | // {"<pkg_name>": {"<cmd_name>": {"parameters": {object_schema}}, ...}, ...} |
| 22 | // Iterate over packages |
| 23 | base::DictionaryValue::Iterator package_iter(json); |
| 24 | while (!package_iter.IsAtEnd()) { |
Alex Vakulenko | fd44869 | 2014-07-22 07:46:53 -0700 | [diff] [blame] | 25 | std::string package_name = package_iter.key(); |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 26 | const base::DictionaryValue* package_value = nullptr; |
| 27 | if (!package_iter.value().GetAsDictionary(&package_value)) { |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 28 | Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, |
| 29 | errors::commands::kTypeMismatch, |
| 30 | "Expecting an object for package '%s'", |
| 31 | package_name.c_str()); |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 32 | return false; |
| 33 | } |
| 34 | // Iterate over command definitions within the current package. |
| 35 | base::DictionaryValue::Iterator command_iter(*package_value); |
| 36 | while (!command_iter.IsAtEnd()) { |
Alex Vakulenko | fd44869 | 2014-07-22 07:46:53 -0700 | [diff] [blame] | 37 | std::string command_name = command_iter.key(); |
| 38 | if (command_name.empty()) { |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 39 | Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, |
| 40 | errors::commands::kInvalidCommandName, |
| 41 | "Unnamed command encountered in package '%s'", |
| 42 | package_name.c_str()); |
Alex Vakulenko | fd44869 | 2014-07-22 07:46:53 -0700 | [diff] [blame] | 43 | return false; |
| 44 | } |
Anton Muhin | 71fb9d5 | 2014-11-21 22:22:39 +0400 | [diff] [blame] | 45 | const base::DictionaryValue* command_def_json = nullptr; |
| 46 | if (!command_iter.value().GetAsDictionary(&command_def_json)) { |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 47 | Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, |
| 48 | errors::commands::kTypeMismatch, |
| 49 | "Expecting an object for command '%s'", |
| 50 | command_name.c_str()); |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 51 | return false; |
| 52 | } |
| 53 | // Construct the compound command name as "pkg_name.cmd_name". |
Vitaly Buka | 24d6fd5 | 2015-08-13 23:22:48 -0700 | [diff] [blame] | 54 | std::string full_command_name = Join(".", package_name, command_name); |
Alex Vakulenko | fd44869 | 2014-07-22 07:46:53 -0700 | [diff] [blame] | 55 | |
Alex Vakulenko | 7e894da | 2015-11-23 11:47:49 -0800 | [diff] [blame] | 56 | auto command_def = CommandDefinition::FromJson(*command_def_json, error); |
| 57 | if (!command_def) { |
| 58 | Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, |
| 59 | errors::commands::kInvalidMinimalRole, |
| 60 | "Error parsing command '%s'", |
| 61 | full_command_name.c_str()); |
| 62 | return false; |
Alex Vakulenko | fd44869 | 2014-07-22 07:46:53 -0700 | [diff] [blame] | 63 | } |
| 64 | |
Vitaly Buka | 52d006a | 2015-11-21 17:14:51 -0800 | [diff] [blame] | 65 | new_defs.insert( |
| 66 | std::make_pair(full_command_name, std::move(command_def))); |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 67 | command_iter.Advance(); |
| 68 | } |
| 69 | package_iter.Advance(); |
| 70 | } |
| 71 | |
| 72 | // Verify that newly loaded command definitions do not override existing |
| 73 | // definitions in another category. This is unlikely, but we don't want to let |
| 74 | // one vendor daemon to define the same commands already handled by another |
| 75 | // daemon on the same device. |
| 76 | for (const auto& pair : new_defs) { |
| 77 | auto iter = definitions_.find(pair.first); |
Vitaly Buka | 453c4dd | 2015-10-04 18:01:50 -0700 | [diff] [blame] | 78 | CHECK(iter == definitions_.end()) << "Definition for command '" |
| 79 | << pair.first |
| 80 | << "' overrides an earlier definition"; |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 81 | } |
| 82 | |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 83 | // Insert new definitions into the global map. |
Alex Vakulenko | 5ef7579 | 2015-03-19 15:50:44 -0700 | [diff] [blame] | 84 | for (auto& pair : new_defs) |
Vitaly Buka | 52d006a | 2015-11-21 17:14:51 -0800 | [diff] [blame] | 85 | definitions_.insert(std::make_pair(pair.first, std::move(pair.second))); |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 86 | return true; |
| 87 | } |
| 88 | |
Alex Vakulenko | 4510944 | 2014-07-29 11:07:10 -0700 | [diff] [blame] | 89 | std::unique_ptr<base::DictionaryValue> CommandDictionary::GetCommandsAsJson( |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 90 | ErrorPtr* error) const { |
Alex Vakulenko | 4510944 | 2014-07-29 11:07:10 -0700 | [diff] [blame] | 91 | std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue); |
| 92 | for (const auto& pair : definitions_) { |
Vitaly Buka | 24d6fd5 | 2015-08-13 23:22:48 -0700 | [diff] [blame] | 93 | auto parts = SplitAtFirst(pair.first, ".", true); |
| 94 | const std::string& package_name = parts.first; |
| 95 | const std::string& command_name = parts.second; |
| 96 | |
Alex Vakulenko | 4510944 | 2014-07-29 11:07:10 -0700 | [diff] [blame] | 97 | base::DictionaryValue* package = nullptr; |
| 98 | if (!dict->GetDictionaryWithoutPathExpansion(package_name, &package)) { |
| 99 | // If this is the first time we encounter this package, create a JSON |
| 100 | // object for it. |
| 101 | package = new base::DictionaryValue; |
| 102 | dict->SetWithoutPathExpansion(package_name, package); |
| 103 | } |
Alex Vakulenko | 7e894da | 2015-11-23 11:47:49 -0800 | [diff] [blame] | 104 | package->SetWithoutPathExpansion(command_name, |
| 105 | pair.second->ToJson().DeepCopy()); |
Alex Vakulenko | 4510944 | 2014-07-29 11:07:10 -0700 | [diff] [blame] | 106 | } |
| 107 | return dict; |
| 108 | } |
| 109 | |
Alex Vakulenko | 5ef7579 | 2015-03-19 15:50:44 -0700 | [diff] [blame] | 110 | const CommandDefinition* CommandDictionary::FindCommand( |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 111 | const std::string& command_name) const { |
| 112 | auto pair = definitions_.find(command_name); |
Alex Vakulenko | 5ef7579 | 2015-03-19 15:50:44 -0700 | [diff] [blame] | 113 | return (pair != definitions_.end()) ? pair->second.get() : nullptr; |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 114 | } |
| 115 | |
Alex Vakulenko | e03af6d | 2015-04-20 11:00:54 -0700 | [diff] [blame] | 116 | CommandDefinition* CommandDictionary::FindCommand( |
| 117 | const std::string& command_name) { |
| 118 | auto pair = definitions_.find(command_name); |
| 119 | return (pair != definitions_.end()) ? pair->second.get() : nullptr; |
| 120 | } |
| 121 | |
Alex Vakulenko | 7c36b67 | 2014-07-16 14:50:58 -0700 | [diff] [blame] | 122 | void CommandDictionary::Clear() { |
| 123 | definitions_.clear(); |
| 124 | } |
| 125 | |
Vitaly Buka | b6f015a | 2015-07-09 14:59:23 -0700 | [diff] [blame] | 126 | } // namespace weave |