blob: 516961f8fd0e3346423c5669639f05712d746b12 [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Alex Vakulenko7c36b672014-07-16 14:50:58 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Stefan Sauer2d16dfa2015-09-25 17:08:35 +02005#include "src/commands/command_dictionary.h"
Alex Vakulenko7c36b672014-07-16 14:50:58 -07006
7#include <base/values.h>
Vitaly Buka7b382ac2015-08-03 13:50:01 -07008#include <weave/enum_to_string.h>
Alex Vakulenko7c36b672014-07-16 14:50:58 -07009
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020010#include "src/commands/command_definition.h"
11#include "src/commands/schema_constants.h"
12#include "src/string_utils.h"
Alex Vakulenko7c36b672014-07-16 14:50:58 -070013
Vitaly Bukab6f015a2015-07-09 14:59:23 -070014namespace weave {
Alex Vakulenko7c36b672014-07-16 14:50:58 -070015
Alex Vakulenko7c36b672014-07-16 14:50:58 -070016bool CommandDictionary::LoadCommands(const base::DictionaryValue& json,
Vitaly Buka0801a1f2015-08-14 10:03:46 -070017 ErrorPtr* error) {
Alex Vakulenko5ef75792015-03-19 15:50:44 -070018 CommandMap new_defs;
Alex Vakulenko7c36b672014-07-16 14:50:58 -070019
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 Vakulenkofd448692014-07-22 07:46:53 -070025 std::string package_name = package_iter.key();
Alex Vakulenko7c36b672014-07-16 14:50:58 -070026 const base::DictionaryValue* package_value = nullptr;
27 if (!package_iter.value().GetAsDictionary(&package_value)) {
Vitaly Buka0801a1f2015-08-14 10:03:46 -070028 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 Vakulenko7c36b672014-07-16 14:50:58 -070032 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 Vakulenkofd448692014-07-22 07:46:53 -070037 std::string command_name = command_iter.key();
38 if (command_name.empty()) {
Vitaly Buka0801a1f2015-08-14 10:03:46 -070039 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 Vakulenkofd448692014-07-22 07:46:53 -070043 return false;
44 }
Anton Muhin71fb9d52014-11-21 22:22:39 +040045 const base::DictionaryValue* command_def_json = nullptr;
46 if (!command_iter.value().GetAsDictionary(&command_def_json)) {
Vitaly Buka0801a1f2015-08-14 10:03:46 -070047 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 Vakulenko7c36b672014-07-16 14:50:58 -070051 return false;
52 }
53 // Construct the compound command name as "pkg_name.cmd_name".
Vitaly Buka24d6fd52015-08-13 23:22:48 -070054 std::string full_command_name = Join(".", package_name, command_name);
Alex Vakulenkofd448692014-07-22 07:46:53 -070055
Alex Vakulenko7e894da2015-11-23 11:47:49 -080056 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 Vakulenkofd448692014-07-22 07:46:53 -070063 }
64
Vitaly Buka52d006a2015-11-21 17:14:51 -080065 new_defs.insert(
66 std::make_pair(full_command_name, std::move(command_def)));
Alex Vakulenko7c36b672014-07-16 14:50:58 -070067 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 Buka453c4dd2015-10-04 18:01:50 -070078 CHECK(iter == definitions_.end()) << "Definition for command '"
79 << pair.first
80 << "' overrides an earlier definition";
Alex Vakulenko7c36b672014-07-16 14:50:58 -070081 }
82
Alex Vakulenko7c36b672014-07-16 14:50:58 -070083 // Insert new definitions into the global map.
Alex Vakulenko5ef75792015-03-19 15:50:44 -070084 for (auto& pair : new_defs)
Vitaly Buka52d006a2015-11-21 17:14:51 -080085 definitions_.insert(std::make_pair(pair.first, std::move(pair.second)));
Alex Vakulenko7c36b672014-07-16 14:50:58 -070086 return true;
87}
88
Alex Vakulenko45109442014-07-29 11:07:10 -070089std::unique_ptr<base::DictionaryValue> CommandDictionary::GetCommandsAsJson(
Vitaly Buka0801a1f2015-08-14 10:03:46 -070090 ErrorPtr* error) const {
Alex Vakulenko45109442014-07-29 11:07:10 -070091 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
92 for (const auto& pair : definitions_) {
Vitaly Buka24d6fd52015-08-13 23:22:48 -070093 auto parts = SplitAtFirst(pair.first, ".", true);
94 const std::string& package_name = parts.first;
95 const std::string& command_name = parts.second;
96
Alex Vakulenko45109442014-07-29 11:07:10 -070097 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 Vakulenko7e894da2015-11-23 11:47:49 -0800104 package->SetWithoutPathExpansion(command_name,
105 pair.second->ToJson().DeepCopy());
Alex Vakulenko45109442014-07-29 11:07:10 -0700106 }
107 return dict;
108}
109
Alex Vakulenko5ef75792015-03-19 15:50:44 -0700110const CommandDefinition* CommandDictionary::FindCommand(
Alex Vakulenko7c36b672014-07-16 14:50:58 -0700111 const std::string& command_name) const {
112 auto pair = definitions_.find(command_name);
Alex Vakulenko5ef75792015-03-19 15:50:44 -0700113 return (pair != definitions_.end()) ? pair->second.get() : nullptr;
Alex Vakulenko7c36b672014-07-16 14:50:58 -0700114}
115
Alex Vakulenkoe03af6d2015-04-20 11:00:54 -0700116CommandDefinition* 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 Vakulenko7c36b672014-07-16 14:50:58 -0700122void CommandDictionary::Clear() {
123 definitions_.clear();
124}
125
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700126} // namespace weave