blob: a23f34d543edbd15cbab6ff38a48a6a59987d1fd [file] [log] [blame]
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -08001// Copyright 2015 The Weave Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Vakulenkoba981152015-12-05 13:58:22 -08005#include "src/component_manager_impl.h"
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -08006
7#include <base/strings/stringprintf.h>
8#include <base/strings/string_number_conversions.h>
9#include <base/strings/string_util.h>
10
11#include "src/commands/schema_constants.h"
Alex Vakulenko7b588fc2015-12-04 16:03:59 -080012#include "src/json_error_codes.h"
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080013#include "src/string_utils.h"
14#include "src/utils.h"
15
16namespace weave {
17
Alex Vakulenko7b588fc2015-12-04 16:03:59 -080018namespace {
19// Max of 100 state update events should be enough in the queue.
20const size_t kMaxStateChangeQueueSize = 100;
Alex Vakulenkod91d6252015-12-05 17:14:39 -080021
22const EnumToStringMap<UserRole>::Map kMap[] = {
23 {UserRole::kViewer, commands::attributes::kCommand_Role_Viewer},
24 {UserRole::kUser, commands::attributes::kCommand_Role_User},
25 {UserRole::kOwner, commands::attributes::kCommand_Role_Owner},
26 {UserRole::kManager, commands::attributes::kCommand_Role_Manager},
27};
28} // anonymous namespace
29
30template <>
31LIBWEAVE_EXPORT EnumToStringMap<UserRole>::EnumToStringMap()
32 : EnumToStringMap(kMap) {}
Alex Vakulenko7b588fc2015-12-04 16:03:59 -080033
Alex Vakulenkoba981152015-12-05 13:58:22 -080034ComponentManagerImpl::ComponentManagerImpl() {}
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080035
Alex Vakulenkoba981152015-12-05 13:58:22 -080036ComponentManagerImpl::ComponentManagerImpl(base::Clock* clock) : clock_{clock} {
37}
38
39ComponentManagerImpl::~ComponentManagerImpl() {}
40
41bool ComponentManagerImpl::AddComponent(const std::string& path,
42 const std::string& name,
43 const std::vector<std::string>& traits,
44 ErrorPtr* error) {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080045 base::DictionaryValue* root = &components_;
46 if (!path.empty()) {
47 root = FindComponentGraftNode(path, error);
48 if (!root)
49 return false;
50 }
51 if (root->GetWithoutPathExpansion(name, nullptr)) {
52 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
53 errors::commands::kInvalidState,
54 "Component '%s' already exists at path '%s'",
55 name.c_str(), path.c_str());
56 return false;
57 }
58
59 // Check to make sure the declared traits are already defined.
60 for (const std::string& trait : traits) {
61 if (!FindTraitDefinition(trait)) {
62 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
63 errors::commands::kInvalidPropValue,
64 "Trait '%s' is undefined", trait.c_str());
65 return false;
66 }
67 }
68 std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue};
69 std::unique_ptr<base::ListValue> traits_list{new base::ListValue};
70 traits_list->AppendStrings(traits);
71 dict->Set("traits", traits_list.release());
72 root->SetWithoutPathExpansion(name, dict.release());
Alex Vakulenko7b588fc2015-12-04 16:03:59 -080073 for (const auto& cb : on_componet_tree_changed_)
74 cb.Run();
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080075 return true;
76}
77
Alex Vakulenkoba981152015-12-05 13:58:22 -080078bool ComponentManagerImpl::AddComponentArrayItem(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080079 const std::string& path,
80 const std::string& name,
81 const std::vector<std::string>& traits,
82 ErrorPtr* error) {
83 base::DictionaryValue* root = &components_;
84 if (!path.empty()) {
85 root = FindComponentGraftNode(path, error);
86 if (!root)
87 return false;
88 }
89 base::ListValue* array_value = nullptr;
90 if (!root->GetListWithoutPathExpansion(name, &array_value)) {
91 array_value = new base::ListValue;
92 root->SetWithoutPathExpansion(name, array_value);
93 }
94 std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue};
95 std::unique_ptr<base::ListValue> traits_list{new base::ListValue};
96 traits_list->AppendStrings(traits);
97 dict->Set("traits", traits_list.release());
98 array_value->Append(dict.release());
Alex Vakulenko7b588fc2015-12-04 16:03:59 -080099 for (const auto& cb : on_componet_tree_changed_)
100 cb.Run();
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800101 return true;
102}
103
Alex Vakulenkoba981152015-12-05 13:58:22 -0800104void ComponentManagerImpl::AddComponentTreeChangedCallback(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800105 const base::Closure& callback) {
106 on_componet_tree_changed_.push_back(callback);
107 callback.Run();
108}
109
Alex Vakulenkoba981152015-12-05 13:58:22 -0800110bool ComponentManagerImpl::LoadTraits(const base::DictionaryValue& dict,
111 ErrorPtr* error) {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800112 bool modified = false;
113 bool result = true;
114 // Check if any of the new traits are already defined. If so, make sure the
115 // definition is exactly the same, or else this is an error.
116 for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
117 if (it.value().GetType() != base::Value::TYPE_DICTIONARY) {
118 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
119 errors::commands::kTypeMismatch,
120 "Trait '%s' must be an object", it.key().c_str());
121 result = false;
122 break;
123 }
124 const base::DictionaryValue* existing_def = nullptr;
125 if (traits_.GetDictionary(it.key(), &existing_def)) {
126 if (!existing_def->Equals(&it.value())) {
127 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
128 errors::commands::kTypeMismatch,
129 "Trait '%s' cannot be redefined",
130 it.key().c_str());
131 result = false;
132 break;
133 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800134 } else {
135 traits_.Set(it.key(), it.value().DeepCopy());
136 modified = true;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800137 }
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800138 }
139
140 if (modified) {
141 for (const auto& cb : on_trait_changed_)
142 cb.Run();
143 }
144 return result;
145}
146
Alex Vakulenkoba981152015-12-05 13:58:22 -0800147bool ComponentManagerImpl::LoadTraits(const std::string& json,
148 ErrorPtr* error) {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800149 std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
150 if (!dict)
151 return false;
152 return LoadTraits(*dict, error);
153}
154
Alex Vakulenkoba981152015-12-05 13:58:22 -0800155void ComponentManagerImpl::AddTraitDefChangedCallback(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800156 const base::Closure& callback) {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800157 on_trait_changed_.push_back(callback);
158 callback.Run();
159}
160
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800161void ComponentManagerImpl::AddCommand(
162 std::unique_ptr<CommandInstance> command_instance) {
163 command_queue_.Add(std::move(command_instance));
164}
165
166std::unique_ptr<CommandInstance> ComponentManagerImpl::ParseCommandInstance(
167 const base::DictionaryValue& command,
168 Command::Origin command_origin,
169 UserRole role,
170 std::string* id,
171 ErrorPtr* error) {
Alex Vakulenkob736c982015-12-05 14:09:27 -0800172 std::string command_id;
173 auto command_instance = CommandInstance::FromJson(&command, command_origin,
174 &command_id, error);
175 // If we fail to validate the command definition, but there was a command ID
176 // specified there, return it to the caller when requested. This will be
177 // used to abort cloud commands.
178 if (id)
179 *id = command_id;
180
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800181 if (!command_instance)
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800182 return nullptr;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800183
184 UserRole minimal_role;
185 if (!GetMinimalRole(command_instance->GetName(), &minimal_role, error))
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800186 return nullptr;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800187
188 if (role < minimal_role) {
189 Error::AddToPrintf(
190 error, FROM_HERE, errors::commands::kDomain, "access_denied",
191 "User role '%s' less than minimal: '%s'", EnumToString(role).c_str(),
192 EnumToString(minimal_role).c_str());
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800193 return nullptr;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800194 }
195
196 std::string component_path = command_instance->GetComponent();
197 if (component_path.empty()) {
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800198 // Find the component to which to route this command. Get the trait name
199 // from the command name and find the first component that has this trait.
200 auto trait_name = SplitAtFirst(command_instance->GetName(), ".", true).first;
201 component_path = FindComponentWithTrait(trait_name);
202 if (component_path.empty()) {
203 Error::AddToPrintf(
204 error, FROM_HERE, errors::commands::kDomain, "unrouted_command",
205 "Unable route command '%s' because there is no component supporting"
206 "trait '%s'", command_instance->GetName().c_str(),
207 trait_name.c_str());
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800208 return nullptr;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800209 }
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800210 command_instance->SetComponent(component_path);
211 }
212
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800213 const base::DictionaryValue* component = FindComponent(component_path, error);
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800214 if (!component)
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800215 return nullptr;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800216
217 // Check that the command's trait is supported by the given component.
218 auto pair = SplitAtFirst(command_instance->GetName(), ".", true);
219
220 bool trait_supported = false;
221 const base::ListValue* supported_traits = nullptr;
222 if (component->GetList("traits", &supported_traits)) {
223 for (const base::Value* value : *supported_traits) {
224 std::string trait;
225 CHECK(value->GetAsString(&trait));
226 if (trait == pair.first) {
227 trait_supported = true;
228 break;
229 }
230 }
231 }
232
233 if (!trait_supported) {
234 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
235 "trait_not_supported",
236 "Component '%s' doesn't support trait '%s'",
237 component_path.c_str(), pair.first.c_str());
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800238 return nullptr;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800239 }
240
Alex Vakulenkob736c982015-12-05 14:09:27 -0800241 if (command_id.empty()) {
242 command_id = std::to_string(++next_command_id_);
243 command_instance->SetID(command_id);
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800244 if (id)
245 *id = command_id;
Alex Vakulenkob736c982015-12-05 14:09:27 -0800246 }
247
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800248 return command_instance;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800249}
250
Alex Vakulenkoba981152015-12-05 13:58:22 -0800251CommandInstance* ComponentManagerImpl::FindCommand(const std::string& id) {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800252 return command_queue_.Find(id);
253}
254
Alex Vakulenkoba981152015-12-05 13:58:22 -0800255void ComponentManagerImpl::AddCommandAddedCallback(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800256 const CommandQueue::CommandCallback& callback) {
257 command_queue_.AddCommandAddedCallback(callback);
258}
259
Alex Vakulenkoba981152015-12-05 13:58:22 -0800260void ComponentManagerImpl::AddCommandRemovedCallback(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800261 const CommandQueue::CommandCallback& callback) {
262 command_queue_.AddCommandRemovedCallback(callback);
263}
264
Alex Vakulenkoba981152015-12-05 13:58:22 -0800265void ComponentManagerImpl::AddCommandHandler(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800266 const std::string& component_path,
267 const std::string& command_name,
268 const Device::CommandHandlerCallback& callback) {
Alex Vakulenko20334332015-12-08 13:19:49 -0800269 // If both component_path and command_name are empty, we are adding the
270 // default handler for all commands.
271 if (!component_path.empty() || !command_name.empty()) {
272 CHECK(FindCommandDefinition(command_name))
273 << "Command undefined: " << command_name;
274 }
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800275 command_queue_.AddCommandHandler(component_path, command_name, callback);
276}
277
Alex Vakulenkoba981152015-12-05 13:58:22 -0800278const base::DictionaryValue* ComponentManagerImpl::FindComponent(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800279 const std::string& path, ErrorPtr* error) const {
280 return FindComponentAt(&components_, path, error);
281}
282
Alex Vakulenkoba981152015-12-05 13:58:22 -0800283const base::DictionaryValue* ComponentManagerImpl::FindTraitDefinition(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800284 const std::string& name) const {
285 const base::DictionaryValue* trait = nullptr;
286 traits_.GetDictionaryWithoutPathExpansion(name, &trait);
287 return trait;
288}
289
Alex Vakulenkoba981152015-12-05 13:58:22 -0800290const base::DictionaryValue* ComponentManagerImpl::FindCommandDefinition(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800291 const std::string& command_name) const {
292 const base::DictionaryValue* definition = nullptr;
293 std::vector<std::string> components = Split(command_name, ".", true, false);
294 // Make sure the |command_name| came in form of trait_name.command_name.
295 if (components.size() != 2)
296 return definition;
297 std::string key = base::StringPrintf("%s.commands.%s", components[0].c_str(),
298 components[1].c_str());
299 traits_.GetDictionary(key, &definition);
300 return definition;
301}
302
Alex Vakulenkoba981152015-12-05 13:58:22 -0800303bool ComponentManagerImpl::GetMinimalRole(const std::string& command_name,
304 UserRole* minimal_role,
305 ErrorPtr* error) const {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800306 const base::DictionaryValue* command = FindCommandDefinition(command_name);
307 if (!command) {
308 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
309 errors::commands::kInvalidCommandName,
310 "Command definition for '%s' not found",
311 command_name.c_str());
312 return false;
313 }
314 std::string value;
315 // The JSON definition has been pre-validated already in LoadCommands, so
316 // just using CHECKs here.
317 CHECK(command->GetString(commands::attributes::kCommand_Role, &value));
318 CHECK(StringToEnum(value, minimal_role));
319 return true;
320}
321
Alex Vakulenkoba981152015-12-05 13:58:22 -0800322void ComponentManagerImpl::AddStateChangedCallback(
323 const base::Closure& callback) {
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800324 on_state_changed_.push_back(callback);
325 callback.Run(); // Force to read current state.
326}
327
Alex Vakulenkoba981152015-12-05 13:58:22 -0800328bool ComponentManagerImpl::SetStateProperties(const std::string& component_path,
329 const base::DictionaryValue& dict,
330 ErrorPtr* error) {
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800331 base::DictionaryValue* component =
332 FindMutableComponent(component_path, error);
333 if (!component)
334 return false;
335
336 base::DictionaryValue* state = nullptr;
337 if (!component->GetDictionary("state", &state)) {
338 state = new base::DictionaryValue;
339 component->Set("state", state);
340 }
341 state->MergeDictionary(&dict);
342 last_state_change_id_++;
343 auto& queue = state_change_queues_[component_path];
344 if (!queue)
345 queue.reset(new StateChangeQueue{kMaxStateChangeQueueSize});
346 base::Time timestamp = clock_ ? clock_->Now() : base::Time::Now();
347 queue->NotifyPropertiesUpdated(timestamp, dict);
348 for (const auto& cb : on_state_changed_)
349 cb.Run();
350 return true;
351}
352
Alex Vakulenkoba981152015-12-05 13:58:22 -0800353bool ComponentManagerImpl::SetStatePropertiesFromJson(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800354 const std::string& component_path,
355 const std::string& json,
356 ErrorPtr* error) {
357 std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
358 return dict && SetStateProperties(component_path, *dict, error);
359}
360
Alex Vakulenkoba981152015-12-05 13:58:22 -0800361const base::Value* ComponentManagerImpl::GetStateProperty(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800362 const std::string& component_path,
363 const std::string& name,
364 ErrorPtr* error) const {
365 const base::DictionaryValue* component = FindComponent(component_path, error);
366 if (!component)
Alex Vakulenko972282c2015-12-08 12:30:04 -0800367 return nullptr;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800368 auto pair = SplitAtFirst(name, ".", true);
369 if (pair.first.empty()) {
370 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
371 errors::commands::kPropertyMissing,
372 "Empty state package in '%s'", name.c_str());
Alex Vakulenko972282c2015-12-08 12:30:04 -0800373 return nullptr;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800374 }
375 if (pair.second.empty()) {
376 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
377 errors::commands::kPropertyMissing,
378 "State property name not specified in '%s'",
379 name.c_str());
Alex Vakulenko972282c2015-12-08 12:30:04 -0800380 return nullptr;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800381 }
382 std::string key = base::StringPrintf("state.%s", name.c_str());
383 const base::Value* value = nullptr;
384 if (!component->Get(key, &value)) {
385 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
386 errors::commands::kPropertyMissing,
387 "State property '%s' not found in component '%s'",
388 name.c_str(), component_path.c_str());
389 }
390 return value;
391}
392
Alex Vakulenkoba981152015-12-05 13:58:22 -0800393bool ComponentManagerImpl::SetStateProperty(const std::string& component_path,
394 const std::string& name,
395 const base::Value& value,
396 ErrorPtr* error) {
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800397 base::DictionaryValue dict;
398 auto pair = SplitAtFirst(name, ".", true);
399 if (pair.first.empty()) {
400 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
401 errors::commands::kPropertyMissing,
402 "Empty state package in '%s'", name.c_str());
403 return false;
404 }
405 if (pair.second.empty()) {
406 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
407 errors::commands::kPropertyMissing,
408 "State property name not specified in '%s'",
409 name.c_str());
410 return false;
411 }
412 dict.Set(name, value.DeepCopy());
413 return SetStateProperties(component_path, dict, error);
414}
415
416ComponentManager::StateSnapshot
Alex Vakulenkoba981152015-12-05 13:58:22 -0800417ComponentManagerImpl::GetAndClearRecordedStateChanges() {
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800418 StateSnapshot snapshot;
419 snapshot.update_id = GetLastStateChangeId();
420 for (auto& pair : state_change_queues_) {
421 auto changes = pair.second->GetAndClearRecordedStateChanges();
422 auto component = pair.first;
423 auto conv = [component](weave::StateChange& change) {
424 return ComponentStateChange{change.timestamp, component,
425 std::move(change.changed_properties)};
426 };
427 std::transform(changes.begin(), changes.end(),
428 std::back_inserter(snapshot.state_changes), conv);
429 }
430
431 // Sort events by the timestamp.
432 auto pred = [](const ComponentStateChange& lhs,
433 const ComponentStateChange& rhs) {
434 return lhs.timestamp < rhs.timestamp;
435 };
436 std::sort(snapshot.state_changes.begin(), snapshot.state_changes.end(), pred);
437 state_change_queues_.clear();
438 return snapshot;
439}
440
Alex Vakulenkoba981152015-12-05 13:58:22 -0800441void ComponentManagerImpl::NotifyStateUpdatedOnServer(UpdateID id) {
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800442 on_server_state_updated_.Notify(id);
443}
444
Alex Vakulenkoba981152015-12-05 13:58:22 -0800445ComponentManager::Token ComponentManagerImpl::AddServerStateUpdatedCallback(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800446 const base::Callback<void(UpdateID)>& callback) {
447 if (state_change_queues_.empty())
448 callback.Run(GetLastStateChangeId());
449 return Token{on_server_state_updated_.Add(callback).release()};
450}
451
Alex Vakulenkoba981152015-12-05 13:58:22 -0800452std::string ComponentManagerImpl::FindComponentWithTrait(
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800453 const std::string& trait) const {
454 for (base::DictionaryValue::Iterator it(components_); !it.IsAtEnd();
455 it.Advance()) {
456 const base::ListValue* supported_traits = nullptr;
457 const base::DictionaryValue* component = nullptr;
458 CHECK(it.value().GetAsDictionary(&component));
459 if (component->GetList("traits", &supported_traits)) {
460 for (const base::Value* value : *supported_traits) {
461 std::string supported_trait;
462 CHECK(value->GetAsString(&supported_trait));
463 if (trait == supported_trait)
464 return it.key();
465 }
466 }
467 }
468 return std::string{};
469}
470
Alex Vakulenko6b394d12015-12-05 15:52:54 -0800471bool ComponentManagerImpl::AddLegacyCommandDefinitions(
472 const base::DictionaryValue& dict,
473 ErrorPtr* error) {
474 bool result = true;
475 bool modified = false;
476 for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
477 const base::DictionaryValue* command_dict = nullptr;
478 if (!it.value().GetAsDictionary(&command_dict)) {
479 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
480 errors::commands::kTypeMismatch,
481 "Package '%s' must be an object", it.key().c_str());
482 result = false;
483 continue;
484 }
485 AddTraitToLegacyComponent(it.key());
486 for (base::DictionaryValue::Iterator it_def(*command_dict);
487 !it_def.IsAtEnd(); it_def.Advance()) {
488 std::string key = base::StringPrintf("%s.commands.%s", it.key().c_str(),
489 it_def.key().c_str());
490 if (traits_.GetDictionary(key, nullptr)) {
491 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
492 errors::commands::kInvalidPropValue,
493 "Redefining command '%s.%s'",
494 it.key().c_str(), it_def.key().c_str());
495 result = false;
496 continue;
497 }
498 traits_.Set(key, it_def.value().DeepCopy());
499 modified = true;
500 }
501 }
502
503 if (modified) {
504 for (const auto& cb : on_trait_changed_)
505 cb.Run();
506 }
507 return result;
508}
509
510bool ComponentManagerImpl::AddLegacyStateDefinitions(
511 const base::DictionaryValue& dict,
512 ErrorPtr* error) {
513 bool result = true;
514 bool modified = false;
515 for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
516 const base::DictionaryValue* state_dict = nullptr;
517 if (!it.value().GetAsDictionary(&state_dict)) {
518 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
519 errors::commands::kTypeMismatch,
520 "Package '%s' must be an object", it.key().c_str());
521 result = false;
522 continue;
523 }
524 AddTraitToLegacyComponent(it.key());
525 for (base::DictionaryValue::Iterator it_def(*state_dict); !it_def.IsAtEnd();
526 it_def.Advance()) {
527 std::string key = base::StringPrintf("%s.state.%s", it.key().c_str(),
528 it_def.key().c_str());
529 if (traits_.GetDictionary(key, nullptr)) {
530 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
531 errors::commands::kInvalidPropValue,
532 "Redefining state property '%s.%s'",
533 it.key().c_str(), it_def.key().c_str());
534 result = false;
535 continue;
536 }
537 traits_.Set(key, it_def.value().DeepCopy());
538 modified = true;
539 }
540 }
541
542 if (modified) {
543 for (const auto& cb : on_trait_changed_)
544 cb.Run();
545 }
546 return result;
547}
548
549const base::DictionaryValue& ComponentManagerImpl::GetLegacyState() const {
550 legacy_state_.Clear();
551 // Build state from components.
552 for (base::DictionaryValue::Iterator it(components_); !it.IsAtEnd();
553 it.Advance()) {
554 const base::DictionaryValue* component_dict = nullptr;
555 const base::DictionaryValue* component_state = nullptr;
556 if (it.value().GetAsDictionary(&component_dict) &&
557 component_dict->GetDictionary("state", &component_state)) {
558 legacy_state_.MergeDictionary(component_state);
559 }
560 }
561 return legacy_state_;
562}
563
564const base::DictionaryValue&
565ComponentManagerImpl::GetLegacyCommandDefinitions() const {
566 legacy_command_defs_.Clear();
567 // Build commandDefs from traits.
568 for (base::DictionaryValue::Iterator it(traits_); !it.IsAtEnd();
569 it.Advance()) {
570 const base::DictionaryValue* trait_dict = nullptr;
571 const base::DictionaryValue* trait_commands = nullptr;
572 if (it.value().GetAsDictionary(&trait_dict) &&
573 trait_dict->GetDictionary("commands", &trait_commands)) {
574 base::DictionaryValue dict;
575 dict.Set(it.key(), trait_commands->DeepCopy());
576 legacy_command_defs_.MergeDictionary(&dict);
577 }
578 }
579 return legacy_command_defs_;
580}
581
582void ComponentManagerImpl::AddTraitToLegacyComponent(const std::string& trait) {
583 // First check if we already have a component supporting this trait.
584 if (!FindComponentWithTrait(trait).empty())
585 return;
586
587 // If not, add this trait to the first component available.
588 base::DictionaryValue* component = nullptr;
589 base::DictionaryValue::Iterator it(components_);
590 if (it.IsAtEnd()) {
591 // No components at all. Create a new one with dummy name.
592 // This normally wouldn't happen since libweave creates its own component
593 // at startup.
594 component = new base::DictionaryValue;
595 components_.Set("__weave__", component);
596 } else {
597 CHECK(components_.GetDictionary(it.key(), &component));
598 }
599 base::ListValue* traits = nullptr;
600 if (!component->GetList("traits", &traits)) {
601 traits = new base::ListValue;
602 component->Set("traits", traits);
603 }
604 traits->AppendString(trait);
605}
606
Alex Vakulenkoba981152015-12-05 13:58:22 -0800607base::DictionaryValue* ComponentManagerImpl::FindComponentGraftNode(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800608 const std::string& path, ErrorPtr* error) {
609 base::DictionaryValue* root = nullptr;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800610 base::DictionaryValue* component = FindMutableComponent(path, error);
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800611 if (component && !component->GetDictionary("components", &root)) {
612 root = new base::DictionaryValue;
613 component->Set("components", root);
614 }
615 return root;
616}
617
Alex Vakulenkoba981152015-12-05 13:58:22 -0800618base::DictionaryValue* ComponentManagerImpl::FindMutableComponent(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800619 const std::string& path,
620 ErrorPtr* error) {
621 return const_cast<base::DictionaryValue*>(
622 FindComponentAt(&components_, path, error));
623}
624
Alex Vakulenkoba981152015-12-05 13:58:22 -0800625const base::DictionaryValue* ComponentManagerImpl::FindComponentAt(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800626 const base::DictionaryValue* root,
627 const std::string& path,
628 ErrorPtr* error) {
629 auto parts = Split(path, ".", true, false);
630 std::string root_path;
631 for (size_t i = 0; i < parts.size(); i++) {
632 auto element = SplitAtFirst(parts[i], "[", true);
633 int array_index = -1;
634 if (element.first.empty()) {
635 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
636 errors::commands::kPropertyMissing,
637 "Empty path element at '%s'", root_path.c_str());
638 return nullptr;
639 }
640 if (!element.second.empty()) {
641 if (element.second.back() != ']') {
642 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
643 errors::commands::kPropertyMissing,
644 "Invalid array element syntax '%s'",
645 parts[i].c_str());
646 return nullptr;
647 }
648 element.second.pop_back();
649 std::string index_str;
650 base::TrimWhitespaceASCII(element.second, base::TrimPositions::TRIM_ALL,
651 &index_str);
652 if (!base::StringToInt(index_str, &array_index) || array_index < 0) {
653 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
654 errors::commands::kInvalidPropValue,
655 "Invalid array index '%s'", element.second.c_str());
656 return nullptr;
657 }
658 }
659
660 if (!root_path.empty()) {
661 // We have processed at least one item in the path before, so now |root|
662 // points to the actual parent component. We need the root to point to
663 // the 'components' element containing child sub-components instead.
664 if (!root->GetDictionary("components", &root)) {
665 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
666 errors::commands::kPropertyMissing,
667 "Component '%s' does not exist at '%s'",
668 element.first.c_str(), root_path.c_str());
669 return nullptr;
670 }
671 }
672
673 const base::Value* value = nullptr;
674 if (!root->GetWithoutPathExpansion(element.first, &value)) {
675 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
676 errors::commands::kPropertyMissing,
677 "Component '%s' does not exist at '%s'",
678 element.first.c_str(), root_path.c_str());
679 return nullptr;
680 }
681
682 if (value->GetType() == base::Value::TYPE_LIST && array_index < 0) {
683 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
684 errors::commands::kTypeMismatch,
685 "Element '%s.%s' is an array",
686 root_path.c_str(), element.first.c_str());
687 return nullptr;
688 }
689 if (value->GetType() == base::Value::TYPE_DICTIONARY && array_index >= 0) {
690 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
691 errors::commands::kTypeMismatch,
692 "Element '%s.%s' is not an array",
693 root_path.c_str(), element.first.c_str());
694 return nullptr;
695 }
696
697 if (value->GetType() == base::Value::TYPE_DICTIONARY) {
698 CHECK(value->GetAsDictionary(&root));
699 } else {
700 const base::ListValue* component_array = nullptr;
701 CHECK(value->GetAsList(&component_array));
702 const base::Value* component_value = nullptr;
703 if (!component_array->Get(array_index, &component_value) ||
704 !component_value->GetAsDictionary(&root)) {
705 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
706 errors::commands::kPropertyMissing,
707 "Element '%s.%s' does not contain item #%d",
708 root_path.c_str(), element.first.c_str(),
709 array_index);
710 return nullptr;
711 }
712 }
713 if (!root_path.empty())
714 root_path += '.';
715 root_path += parts[i];
716 }
717 return root;
718}
719
720} // namespace weave