blob: 3ea1f46fbf490a02e1125b2796069a8fb82d8f58 [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
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -08007#include <base/strings/string_number_conversions.h>
8#include <base/strings/string_util.h>
Vitaly Buka34668e72015-12-15 14:46:47 -08009#include <base/strings/stringprintf.h>
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080010
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
Alex Vakulenko1054d3e2016-02-23 15:18:15 -080022const char kMinimalRole[] = "minimalRole";
23
Alex Vakulenkod91d6252015-12-05 17:14:39 -080024const EnumToStringMap<UserRole>::Map kMap[] = {
Alex Vakulenko1054d3e2016-02-23 15:18:15 -080025 {UserRole::kViewer, "viewer"},
26 {UserRole::kUser, "user"},
27 {UserRole::kOwner, "owner"},
28 {UserRole::kManager, "manager"},
Alex Vakulenkod91d6252015-12-05 17:14:39 -080029};
30} // anonymous namespace
31
32template <>
33LIBWEAVE_EXPORT EnumToStringMap<UserRole>::EnumToStringMap()
34 : EnumToStringMap(kMap) {}
Alex Vakulenko7b588fc2015-12-04 16:03:59 -080035
Alex Vakulenko98d1fee2016-02-01 12:25:21 -080036ComponentManagerImpl::ComponentManagerImpl(provider::TaskRunner* task_runner,
37 base::Clock* clock)
38 : clock_{clock ? clock : &default_clock_},
39 command_queue_{task_runner, clock_} {}
Alex Vakulenkoba981152015-12-05 13:58:22 -080040
41ComponentManagerImpl::~ComponentManagerImpl() {}
42
43bool ComponentManagerImpl::AddComponent(const std::string& path,
44 const std::string& name,
45 const std::vector<std::string>& traits,
46 ErrorPtr* error) {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080047 base::DictionaryValue* root = &components_;
48 if (!path.empty()) {
49 root = FindComponentGraftNode(path, error);
50 if (!root)
51 return false;
52 }
53 if (root->GetWithoutPathExpansion(name, nullptr)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -080054 return Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidState,
55 "Component '%s' already exists at path '%s'",
56 name.c_str(), path.c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080057 }
58
59 // Check to make sure the declared traits are already defined.
60 for (const std::string& trait : traits) {
61 if (!FindTraitDefinition(trait)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -080062 return Error::AddToPrintf(error, FROM_HERE,
63 errors::commands::kInvalidPropValue,
64 "Trait '%s' is undefined", trait.c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080065 }
66 }
67 std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue};
68 std::unique_ptr<base::ListValue> traits_list{new base::ListValue};
69 traits_list->AppendStrings(traits);
70 dict->Set("traits", traits_list.release());
71 root->SetWithoutPathExpansion(name, dict.release());
Alex Vakulenko7b588fc2015-12-04 16:03:59 -080072 for (const auto& cb : on_componet_tree_changed_)
73 cb.Run();
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080074 return true;
75}
76
Alex Vakulenkoba981152015-12-05 13:58:22 -080077bool ComponentManagerImpl::AddComponentArrayItem(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -080078 const std::string& path,
79 const std::string& name,
80 const std::vector<std::string>& traits,
81 ErrorPtr* error) {
82 base::DictionaryValue* root = &components_;
83 if (!path.empty()) {
84 root = FindComponentGraftNode(path, error);
85 if (!root)
86 return false;
87 }
88 base::ListValue* array_value = nullptr;
89 if (!root->GetListWithoutPathExpansion(name, &array_value)) {
90 array_value = new base::ListValue;
91 root->SetWithoutPathExpansion(name, array_value);
92 }
93 std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue};
94 std::unique_ptr<base::ListValue> traits_list{new base::ListValue};
95 traits_list->AppendStrings(traits);
96 dict->Set("traits", traits_list.release());
97 array_value->Append(dict.release());
Alex Vakulenko7b588fc2015-12-04 16:03:59 -080098 for (const auto& cb : on_componet_tree_changed_)
99 cb.Run();
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800100 return true;
101}
102
Alex Vakulenkoce850b52016-01-04 09:27:50 -0800103bool ComponentManagerImpl::RemoveComponent(const std::string& path,
104 const std::string& name,
105 ErrorPtr* error) {
106 base::DictionaryValue* root = &components_;
107 if (!path.empty()) {
108 root = FindComponentGraftNode(path, error);
109 if (!root)
110 return false;
111 }
112
113 if (!root->RemoveWithoutPathExpansion(name, nullptr)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800114 return Error::AddToPrintf(error, FROM_HERE, errors::commands::kInvalidState,
115 "Component '%s' does not exist at path '%s'",
116 name.c_str(), path.c_str());
Alex Vakulenkoce850b52016-01-04 09:27:50 -0800117 }
118
119 for (const auto& cb : on_componet_tree_changed_)
120 cb.Run();
121 return true;
122}
123
124bool ComponentManagerImpl::RemoveComponentArrayItem(const std::string& path,
125 const std::string& name,
126 size_t index,
127 ErrorPtr* error) {
128 base::DictionaryValue* root = &components_;
129 if (!path.empty()) {
130 root = FindComponentGraftNode(path, error);
131 if (!root)
132 return false;
133 }
134
135 base::ListValue* array_value = nullptr;
136 if (!root->GetListWithoutPathExpansion(name, &array_value)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800137 return Error::AddToPrintf(
138 error, FROM_HERE, errors::commands::kInvalidState,
139 "There is no component array named '%s' at path '%s'", name.c_str(),
140 path.c_str());
Alex Vakulenkoce850b52016-01-04 09:27:50 -0800141 }
142
143 if (!array_value->Remove(index, nullptr)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800144 return Error::AddToPrintf(
Vitaly Buka48a86692016-01-21 17:15:58 -0800145 error, FROM_HERE, errors::commands::kInvalidState,
Alex Vakulenkoce850b52016-01-04 09:27:50 -0800146 "Component array '%s' at path '%s' does not have an element %zu",
147 name.c_str(), path.c_str(), index);
Alex Vakulenkoce850b52016-01-04 09:27:50 -0800148 }
149
150 for (const auto& cb : on_componet_tree_changed_)
151 cb.Run();
152 return true;
153}
154
Alex Vakulenkoba981152015-12-05 13:58:22 -0800155void ComponentManagerImpl::AddComponentTreeChangedCallback(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800156 const base::Closure& callback) {
157 on_componet_tree_changed_.push_back(callback);
158 callback.Run();
159}
160
Alex Vakulenkoba981152015-12-05 13:58:22 -0800161bool ComponentManagerImpl::LoadTraits(const base::DictionaryValue& dict,
162 ErrorPtr* error) {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800163 bool modified = false;
164 bool result = true;
165 // Check if any of the new traits are already defined. If so, make sure the
166 // definition is exactly the same, or else this is an error.
167 for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
168 if (it.value().GetType() != base::Value::TYPE_DICTIONARY) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800169 Error::AddToPrintf(error, FROM_HERE, errors::commands::kTypeMismatch,
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800170 "Trait '%s' must be an object", it.key().c_str());
171 result = false;
172 break;
173 }
174 const base::DictionaryValue* existing_def = nullptr;
175 if (traits_.GetDictionary(it.key(), &existing_def)) {
176 if (!existing_def->Equals(&it.value())) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800177 Error::AddToPrintf(error, FROM_HERE, errors::commands::kTypeMismatch,
Vitaly Buka34668e72015-12-15 14:46:47 -0800178 "Trait '%s' cannot be redefined", it.key().c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800179 result = false;
180 break;
181 }
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800182 } else {
183 traits_.Set(it.key(), it.value().DeepCopy());
184 modified = true;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800185 }
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800186 }
187
188 if (modified) {
189 for (const auto& cb : on_trait_changed_)
190 cb.Run();
191 }
192 return result;
193}
194
Alex Vakulenkoba981152015-12-05 13:58:22 -0800195bool ComponentManagerImpl::LoadTraits(const std::string& json,
196 ErrorPtr* error) {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800197 std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
198 if (!dict)
199 return false;
200 return LoadTraits(*dict, error);
201}
202
Alex Vakulenkoba981152015-12-05 13:58:22 -0800203void ComponentManagerImpl::AddTraitDefChangedCallback(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800204 const base::Closure& callback) {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800205 on_trait_changed_.push_back(callback);
206 callback.Run();
207}
208
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800209void ComponentManagerImpl::AddCommand(
210 std::unique_ptr<CommandInstance> command_instance) {
211 command_queue_.Add(std::move(command_instance));
212}
213
214std::unique_ptr<CommandInstance> ComponentManagerImpl::ParseCommandInstance(
215 const base::DictionaryValue& command,
216 Command::Origin command_origin,
217 UserRole role,
218 std::string* id,
219 ErrorPtr* error) {
Alex Vakulenkob736c982015-12-05 14:09:27 -0800220 std::string command_id;
Vitaly Buka34668e72015-12-15 14:46:47 -0800221 auto command_instance =
222 CommandInstance::FromJson(&command, command_origin, &command_id, error);
Alex Vakulenkob736c982015-12-05 14:09:27 -0800223 // If we fail to validate the command definition, but there was a command ID
224 // specified there, return it to the caller when requested. This will be
225 // used to abort cloud commands.
226 if (id)
227 *id = command_id;
228
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800229 if (!command_instance)
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800230 return nullptr;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800231
232 UserRole minimal_role;
233 if (!GetMinimalRole(command_instance->GetName(), &minimal_role, error))
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800234 return nullptr;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800235
236 if (role < minimal_role) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800237 return Error::AddToPrintf(error, FROM_HERE, "access_denied",
238 "User role '%s' less than minimal: '%s'",
239 EnumToString(role).c_str(),
240 EnumToString(minimal_role).c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800241 }
242
243 std::string component_path = command_instance->GetComponent();
244 if (component_path.empty()) {
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800245 // Find the component to which to route this command. Get the trait name
246 // from the command name and find the first component that has this trait.
Vitaly Buka34668e72015-12-15 14:46:47 -0800247 auto trait_name =
248 SplitAtFirst(command_instance->GetName(), ".", true).first;
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800249 component_path = FindComponentWithTrait(trait_name);
250 if (component_path.empty()) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800251 return Error::AddToPrintf(
Vitaly Buka48a86692016-01-21 17:15:58 -0800252 error, FROM_HERE, "unrouted_command",
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800253 "Unable route command '%s' because there is no component supporting"
Vitaly Buka34668e72015-12-15 14:46:47 -0800254 "trait '%s'",
255 command_instance->GetName().c_str(), trait_name.c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800256 }
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800257 command_instance->SetComponent(component_path);
258 }
259
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800260 const base::DictionaryValue* component = FindComponent(component_path, error);
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800261 if (!component)
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800262 return nullptr;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800263
264 // Check that the command's trait is supported by the given component.
265 auto pair = SplitAtFirst(command_instance->GetName(), ".", true);
266
267 bool trait_supported = false;
268 const base::ListValue* supported_traits = nullptr;
269 if (component->GetList("traits", &supported_traits)) {
270 for (const base::Value* value : *supported_traits) {
271 std::string trait;
272 CHECK(value->GetAsString(&trait));
273 if (trait == pair.first) {
274 trait_supported = true;
275 break;
276 }
277 }
278 }
279
280 if (!trait_supported) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800281 return Error::AddToPrintf(error, FROM_HERE, "trait_not_supported",
282 "Component '%s' doesn't support trait '%s'",
283 component_path.c_str(), pair.first.c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800284 }
285
Alex Vakulenkob736c982015-12-05 14:09:27 -0800286 if (command_id.empty()) {
287 command_id = std::to_string(++next_command_id_);
288 command_instance->SetID(command_id);
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800289 if (id)
290 *id = command_id;
Alex Vakulenkob736c982015-12-05 14:09:27 -0800291 }
292
Alex Vakulenkod91d6252015-12-05 17:14:39 -0800293 return command_instance;
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800294}
295
Alex Vakulenkoba981152015-12-05 13:58:22 -0800296CommandInstance* ComponentManagerImpl::FindCommand(const std::string& id) {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800297 return command_queue_.Find(id);
298}
299
Alex Vakulenkoba981152015-12-05 13:58:22 -0800300void ComponentManagerImpl::AddCommandAddedCallback(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800301 const CommandQueue::CommandCallback& callback) {
302 command_queue_.AddCommandAddedCallback(callback);
303}
304
Alex Vakulenkoba981152015-12-05 13:58:22 -0800305void ComponentManagerImpl::AddCommandRemovedCallback(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800306 const CommandQueue::CommandCallback& callback) {
307 command_queue_.AddCommandRemovedCallback(callback);
308}
309
Alex Vakulenkoba981152015-12-05 13:58:22 -0800310void ComponentManagerImpl::AddCommandHandler(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800311 const std::string& component_path,
312 const std::string& command_name,
313 const Device::CommandHandlerCallback& callback) {
Alex Vakulenko20334332015-12-08 13:19:49 -0800314 // If both component_path and command_name are empty, we are adding the
315 // default handler for all commands.
316 if (!component_path.empty() || !command_name.empty()) {
Vitaly Buka34668e72015-12-15 14:46:47 -0800317 CHECK(FindCommandDefinition(command_name)) << "Command undefined: "
318 << command_name;
Alex Vakulenko20334332015-12-08 13:19:49 -0800319 }
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800320 command_queue_.AddCommandHandler(component_path, command_name, callback);
321}
322
Alex Vakulenkoba981152015-12-05 13:58:22 -0800323const base::DictionaryValue* ComponentManagerImpl::FindComponent(
Vitaly Buka34668e72015-12-15 14:46:47 -0800324 const std::string& path,
325 ErrorPtr* error) const {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800326 return FindComponentAt(&components_, path, error);
327}
328
Alex Vakulenkoba981152015-12-05 13:58:22 -0800329const base::DictionaryValue* ComponentManagerImpl::FindTraitDefinition(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800330 const std::string& name) const {
331 const base::DictionaryValue* trait = nullptr;
332 traits_.GetDictionaryWithoutPathExpansion(name, &trait);
333 return trait;
334}
335
Alex Vakulenkoba981152015-12-05 13:58:22 -0800336const base::DictionaryValue* ComponentManagerImpl::FindCommandDefinition(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800337 const std::string& command_name) const {
338 const base::DictionaryValue* definition = nullptr;
339 std::vector<std::string> components = Split(command_name, ".", true, false);
340 // Make sure the |command_name| came in form of trait_name.command_name.
341 if (components.size() != 2)
342 return definition;
343 std::string key = base::StringPrintf("%s.commands.%s", components[0].c_str(),
344 components[1].c_str());
345 traits_.GetDictionary(key, &definition);
346 return definition;
347}
348
Alex Vakulenkoba981152015-12-05 13:58:22 -0800349bool ComponentManagerImpl::GetMinimalRole(const std::string& command_name,
350 UserRole* minimal_role,
351 ErrorPtr* error) const {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800352 const base::DictionaryValue* command = FindCommandDefinition(command_name);
353 if (!command) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800354 return Error::AddToPrintf(
355 error, FROM_HERE, errors::commands::kInvalidCommandName,
356 "Command definition for '%s' not found", command_name.c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800357 }
358 std::string value;
359 // The JSON definition has been pre-validated already in LoadCommands, so
360 // just using CHECKs here.
Alex Vakulenko1054d3e2016-02-23 15:18:15 -0800361 CHECK(command->GetString(kMinimalRole, &value));
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800362 CHECK(StringToEnum(value, minimal_role));
363 return true;
364}
365
Alex Vakulenkoba981152015-12-05 13:58:22 -0800366void ComponentManagerImpl::AddStateChangedCallback(
367 const base::Closure& callback) {
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800368 on_state_changed_.push_back(callback);
369 callback.Run(); // Force to read current state.
370}
371
Alex Vakulenkoba981152015-12-05 13:58:22 -0800372bool ComponentManagerImpl::SetStateProperties(const std::string& component_path,
373 const base::DictionaryValue& dict,
374 ErrorPtr* error) {
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800375 base::DictionaryValue* component =
376 FindMutableComponent(component_path, error);
377 if (!component)
378 return false;
379
380 base::DictionaryValue* state = nullptr;
381 if (!component->GetDictionary("state", &state)) {
382 state = new base::DictionaryValue;
383 component->Set("state", state);
384 }
385 state->MergeDictionary(&dict);
386 last_state_change_id_++;
387 auto& queue = state_change_queues_[component_path];
388 if (!queue)
389 queue.reset(new StateChangeQueue{kMaxStateChangeQueueSize});
Vitaly Bukaece713e2015-12-09 10:59:33 -0800390 base::Time timestamp = clock_->Now();
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800391 queue->NotifyPropertiesUpdated(timestamp, dict);
392 for (const auto& cb : on_state_changed_)
393 cb.Run();
394 return true;
395}
396
Alex Vakulenkoba981152015-12-05 13:58:22 -0800397bool ComponentManagerImpl::SetStatePropertiesFromJson(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800398 const std::string& component_path,
399 const std::string& json,
400 ErrorPtr* error) {
401 std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
402 return dict && SetStateProperties(component_path, *dict, error);
403}
404
Alex Vakulenkoba981152015-12-05 13:58:22 -0800405const base::Value* ComponentManagerImpl::GetStateProperty(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800406 const std::string& component_path,
407 const std::string& name,
408 ErrorPtr* error) const {
409 const base::DictionaryValue* component = FindComponent(component_path, error);
410 if (!component)
Alex Vakulenko972282c2015-12-08 12:30:04 -0800411 return nullptr;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800412 auto pair = SplitAtFirst(name, ".", true);
413 if (pair.first.empty()) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800414 return Error::AddToPrintf(error, FROM_HERE,
415 errors::commands::kPropertyMissing,
416 "Empty state package in '%s'", name.c_str());
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800417 }
418 if (pair.second.empty()) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800419 return Error::AddToPrintf(
420 error, FROM_HERE, errors::commands::kPropertyMissing,
421 "State property name not specified in '%s'", name.c_str());
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800422 }
423 std::string key = base::StringPrintf("state.%s", name.c_str());
424 const base::Value* value = nullptr;
425 if (!component->Get(key, &value)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800426 return Error::AddToPrintf(error, FROM_HERE,
427 errors::commands::kPropertyMissing,
428 "State property '%s' not found in component '%s'",
429 name.c_str(), component_path.c_str());
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800430 }
431 return value;
432}
433
Alex Vakulenkoba981152015-12-05 13:58:22 -0800434bool ComponentManagerImpl::SetStateProperty(const std::string& component_path,
435 const std::string& name,
436 const base::Value& value,
437 ErrorPtr* error) {
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800438 base::DictionaryValue dict;
439 auto pair = SplitAtFirst(name, ".", true);
440 if (pair.first.empty()) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800441 return Error::AddToPrintf(error, FROM_HERE,
442 errors::commands::kPropertyMissing,
443 "Empty state package in '%s'", name.c_str());
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800444 }
445 if (pair.second.empty()) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800446 return Error::AddToPrintf(
447 error, FROM_HERE, errors::commands::kPropertyMissing,
448 "State property name not specified in '%s'", name.c_str());
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800449 }
450 dict.Set(name, value.DeepCopy());
451 return SetStateProperties(component_path, dict, error);
452}
453
454ComponentManager::StateSnapshot
Alex Vakulenkoba981152015-12-05 13:58:22 -0800455ComponentManagerImpl::GetAndClearRecordedStateChanges() {
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800456 StateSnapshot snapshot;
457 snapshot.update_id = GetLastStateChangeId();
458 for (auto& pair : state_change_queues_) {
459 auto changes = pair.second->GetAndClearRecordedStateChanges();
460 auto component = pair.first;
461 auto conv = [component](weave::StateChange& change) {
462 return ComponentStateChange{change.timestamp, component,
463 std::move(change.changed_properties)};
464 };
465 std::transform(changes.begin(), changes.end(),
466 std::back_inserter(snapshot.state_changes), conv);
467 }
468
469 // Sort events by the timestamp.
470 auto pred = [](const ComponentStateChange& lhs,
471 const ComponentStateChange& rhs) {
472 return lhs.timestamp < rhs.timestamp;
473 };
474 std::sort(snapshot.state_changes.begin(), snapshot.state_changes.end(), pred);
475 state_change_queues_.clear();
476 return snapshot;
477}
478
Alex Vakulenkoba981152015-12-05 13:58:22 -0800479void ComponentManagerImpl::NotifyStateUpdatedOnServer(UpdateID id) {
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800480 on_server_state_updated_.Notify(id);
481}
482
Alex Vakulenkoba981152015-12-05 13:58:22 -0800483ComponentManager::Token ComponentManagerImpl::AddServerStateUpdatedCallback(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800484 const base::Callback<void(UpdateID)>& callback) {
485 if (state_change_queues_.empty())
486 callback.Run(GetLastStateChangeId());
487 return Token{on_server_state_updated_.Add(callback).release()};
488}
489
Alex Vakulenkoba981152015-12-05 13:58:22 -0800490std::string ComponentManagerImpl::FindComponentWithTrait(
Alex Vakulenkoa3c5e6d2015-12-04 17:54:38 -0800491 const std::string& trait) const {
492 for (base::DictionaryValue::Iterator it(components_); !it.IsAtEnd();
493 it.Advance()) {
494 const base::ListValue* supported_traits = nullptr;
495 const base::DictionaryValue* component = nullptr;
496 CHECK(it.value().GetAsDictionary(&component));
497 if (component->GetList("traits", &supported_traits)) {
498 for (const base::Value* value : *supported_traits) {
499 std::string supported_trait;
500 CHECK(value->GetAsString(&supported_trait));
501 if (trait == supported_trait)
502 return it.key();
503 }
504 }
505 }
506 return std::string{};
507}
508
Alex Vakulenkoba981152015-12-05 13:58:22 -0800509base::DictionaryValue* ComponentManagerImpl::FindComponentGraftNode(
Vitaly Buka34668e72015-12-15 14:46:47 -0800510 const std::string& path,
511 ErrorPtr* error) {
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800512 base::DictionaryValue* root = nullptr;
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800513 base::DictionaryValue* component = FindMutableComponent(path, error);
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800514 if (component && !component->GetDictionary("components", &root)) {
515 root = new base::DictionaryValue;
516 component->Set("components", root);
517 }
518 return root;
519}
520
Alex Vakulenkoba981152015-12-05 13:58:22 -0800521base::DictionaryValue* ComponentManagerImpl::FindMutableComponent(
Alex Vakulenko7b588fc2015-12-04 16:03:59 -0800522 const std::string& path,
523 ErrorPtr* error) {
524 return const_cast<base::DictionaryValue*>(
525 FindComponentAt(&components_, path, error));
526}
527
Alex Vakulenkoba981152015-12-05 13:58:22 -0800528const base::DictionaryValue* ComponentManagerImpl::FindComponentAt(
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800529 const base::DictionaryValue* root,
530 const std::string& path,
531 ErrorPtr* error) {
532 auto parts = Split(path, ".", true, false);
533 std::string root_path;
534 for (size_t i = 0; i < parts.size(); i++) {
535 auto element = SplitAtFirst(parts[i], "[", true);
536 int array_index = -1;
537 if (element.first.empty()) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800538 return Error::AddToPrintf(
539 error, FROM_HERE, errors::commands::kPropertyMissing,
540 "Empty path element at '%s'", root_path.c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800541 }
542 if (!element.second.empty()) {
543 if (element.second.back() != ']') {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800544 return Error::AddToPrintf(
545 error, FROM_HERE, errors::commands::kPropertyMissing,
546 "Invalid array element syntax '%s'", parts[i].c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800547 }
548 element.second.pop_back();
549 std::string index_str;
550 base::TrimWhitespaceASCII(element.second, base::TrimPositions::TRIM_ALL,
551 &index_str);
552 if (!base::StringToInt(index_str, &array_index) || array_index < 0) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800553 return Error::AddToPrintf(
554 error, FROM_HERE, errors::commands::kInvalidPropValue,
555 "Invalid array index '%s'", element.second.c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800556 }
557 }
558
559 if (!root_path.empty()) {
560 // We have processed at least one item in the path before, so now |root|
561 // points to the actual parent component. We need the root to point to
562 // the 'components' element containing child sub-components instead.
563 if (!root->GetDictionary("components", &root)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800564 return Error::AddToPrintf(error, FROM_HERE,
565 errors::commands::kPropertyMissing,
566 "Component '%s' does not exist at '%s'",
567 element.first.c_str(), root_path.c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800568 }
569 }
570
571 const base::Value* value = nullptr;
572 if (!root->GetWithoutPathExpansion(element.first, &value)) {
Vitaly Buka48a86692016-01-21 17:15:58 -0800573 Error::AddToPrintf(error, FROM_HERE, errors::commands::kPropertyMissing,
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800574 "Component '%s' does not exist at '%s'",
575 element.first.c_str(), root_path.c_str());
576 return nullptr;
577 }
578
579 if (value->GetType() == base::Value::TYPE_LIST && array_index < 0) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800580 return Error::AddToPrintf(error, FROM_HERE,
581 errors::commands::kTypeMismatch,
582 "Element '%s.%s' is an array",
583 root_path.c_str(), element.first.c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800584 }
585 if (value->GetType() == base::Value::TYPE_DICTIONARY && array_index >= 0) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800586 return Error::AddToPrintf(error, FROM_HERE,
587 errors::commands::kTypeMismatch,
588 "Element '%s.%s' is not an array",
589 root_path.c_str(), element.first.c_str());
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800590 }
591
592 if (value->GetType() == base::Value::TYPE_DICTIONARY) {
593 CHECK(value->GetAsDictionary(&root));
594 } else {
595 const base::ListValue* component_array = nullptr;
596 CHECK(value->GetAsList(&component_array));
597 const base::Value* component_value = nullptr;
598 if (!component_array->Get(array_index, &component_value) ||
599 !component_value->GetAsDictionary(&root)) {
Vitaly Buka0dbbf602016-01-22 11:38:37 -0800600 return Error::AddToPrintf(
601 error, FROM_HERE, errors::commands::kPropertyMissing,
602 "Element '%s.%s' does not contain item #%d", root_path.c_str(),
603 element.first.c_str(), array_index);
Alex Vakulenko44c1dbe2015-12-03 15:35:09 -0800604 }
605 }
606 if (!root_path.empty())
607 root_path += '.';
608 root_path += parts[i];
609 }
610 return root;
611}
612
613} // namespace weave