blob: be6cb169bb800fefe1c24c4abab1eb8bf247e41a [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Alex Vakulenko66ec2922014-06-17 15:30:22 -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/schema_utils.h"
Alex Vakulenko66ec2922014-06-17 15:30:22 -07006
7#include <algorithm>
8#include <set>
9#include <string>
10
11#include <base/json/json_writer.h>
Vitaly Buka0d501072015-08-18 18:09:46 -070012#include <base/logging.h>
Alex Vakulenko66ec2922014-06-17 15:30:22 -070013
Stefan Sauer2d16dfa2015-09-25 17:08:35 +020014#include "src/commands/object_schema.h"
15#include "src/commands/prop_types.h"
16#include "src/commands/prop_values.h"
Alex Vakulenko66ec2922014-06-17 15:30:22 -070017
Vitaly Bukab6f015a2015-07-09 14:59:23 -070018namespace weave {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070019namespace {
20// Helper function to report "type mismatch" errors when parsing JSON.
Alex Vakulenko7e8df462015-07-07 10:59:20 -070021void ReportJsonTypeMismatch(const tracked_objects::Location& location,
22 const base::Value* value_in,
Alex Vakulenko66ec2922014-06-17 15:30:22 -070023 const std::string& expected_type,
Vitaly Buka0801a1f2015-08-14 10:03:46 -070024 ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070025 std::string value_as_string;
Alex Vakulenkoae1ffbc2015-06-15 12:53:22 -070026 base::JSONWriter::Write(*value_in, &value_as_string);
Vitaly Buka0801a1f2015-08-14 10:03:46 -070027 Error::AddToPrintf(error, location, errors::commands::kDomain,
28 errors::commands::kTypeMismatch,
29 "Unable to convert value %s into %s",
30 value_as_string.c_str(), expected_type.c_str());
Alex Vakulenko66ec2922014-06-17 15:30:22 -070031}
32
33// Template version of ReportJsonTypeMismatch that deduces the type of expected
34// data from the value_out parameter passed to particular overload of
35// TypedValueFromJson() function. Always returns false.
Vitaly Bukaa647c852015-07-06 14:51:01 -070036template <typename T>
Alex Vakulenko7e8df462015-07-07 10:59:20 -070037bool ReportUnexpectedJson(const tracked_objects::Location& location,
38 const base::Value* value_in,
Vitaly Bukaa647c852015-07-06 14:51:01 -070039 T*,
Vitaly Buka0801a1f2015-08-14 10:03:46 -070040 ErrorPtr* error) {
Alex Vakulenko7e8df462015-07-07 10:59:20 -070041 ReportJsonTypeMismatch(location, value_in,
42 PropType::GetTypeStringFromType(GetValueType<T>()),
43 error);
Alex Vakulenko66ec2922014-06-17 15:30:22 -070044 return false;
45}
46
Vitaly Buka0801a1f2015-08-14 10:03:46 -070047bool ErrorMissingProperty(ErrorPtr* error,
Alex Vakulenko7e8df462015-07-07 10:59:20 -070048 const tracked_objects::Location& location,
49 const char* param_name) {
Vitaly Buka0801a1f2015-08-14 10:03:46 -070050 Error::AddToPrintf(error, location, errors::commands::kDomain,
51 errors::commands::kPropertyMissing,
52 "Required parameter missing: %s", param_name);
Alex Vakulenko66ec2922014-06-17 15:30:22 -070053 return false;
54}
Vitaly Buka8d8d2192015-07-21 22:25:09 -070055
Alex Vakulenko66ec2922014-06-17 15:30:22 -070056} // namespace
57
58// Specializations of TypedValueToJson<T>() for supported C++ types.
Vitaly Buka6942e1f2015-07-28 15:33:55 -070059std::unique_ptr<base::FundamentalValue> TypedValueToJson(bool value) {
Vitaly Buka39ef1952015-07-21 20:32:11 -070060 return std::unique_ptr<base::FundamentalValue>(
61 new base::FundamentalValue(value));
Alex Vakulenko66ec2922014-06-17 15:30:22 -070062}
63
Vitaly Buka6942e1f2015-07-28 15:33:55 -070064std::unique_ptr<base::FundamentalValue> TypedValueToJson(int value) {
Vitaly Buka39ef1952015-07-21 20:32:11 -070065 return std::unique_ptr<base::FundamentalValue>(
66 new base::FundamentalValue(value));
Alex Vakulenko66ec2922014-06-17 15:30:22 -070067}
68
Vitaly Buka6942e1f2015-07-28 15:33:55 -070069std::unique_ptr<base::FundamentalValue> TypedValueToJson(double value) {
Vitaly Buka39ef1952015-07-21 20:32:11 -070070 return std::unique_ptr<base::FundamentalValue>(
71 new base::FundamentalValue(value));
Alex Vakulenko66ec2922014-06-17 15:30:22 -070072}
73
Vitaly Buka6942e1f2015-07-28 15:33:55 -070074std::unique_ptr<base::StringValue> TypedValueToJson(const std::string& value) {
Vitaly Buka39ef1952015-07-21 20:32:11 -070075 return std::unique_ptr<base::StringValue>(new base::StringValue(value));
Alex Vakulenko66ec2922014-06-17 15:30:22 -070076}
77
Vitaly Buka6942e1f2015-07-28 15:33:55 -070078std::unique_ptr<base::DictionaryValue> TypedValueToJson(const ValueMap& value) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070079 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
80 for (const auto& pair : value) {
Vitaly Buka6942e1f2015-07-28 15:33:55 -070081 auto prop_value = pair.second->ToJson();
82 CHECK(prop_value);
Alex Vakulenko66ec2922014-06-17 15:30:22 -070083 dict->SetWithoutPathExpansion(pair.first, prop_value.release());
84 }
Vitaly Buka39ef1952015-07-21 20:32:11 -070085 return dict;
Alex Vakulenko66ec2922014-06-17 15:30:22 -070086}
87
Vitaly Buka6942e1f2015-07-28 15:33:55 -070088std::unique_ptr<base::ListValue> TypedValueToJson(const ValueVector& value) {
Alex Vakulenko29e64442015-03-20 13:59:19 -070089 std::unique_ptr<base::ListValue> list(new base::ListValue);
90 for (const auto& item : value) {
Vitaly Buka6942e1f2015-07-28 15:33:55 -070091 auto json = item->ToJson();
92 CHECK(json);
Alex Vakulenko29e64442015-03-20 13:59:19 -070093 list->Append(json.release());
94 }
Vitaly Buka39ef1952015-07-21 20:32:11 -070095 return list;
Alex Vakulenko29e64442015-03-20 13:59:19 -070096}
97
Alex Vakulenko66ec2922014-06-17 15:30:22 -070098bool TypedValueFromJson(const base::Value* value_in,
Alex Vakulenkod94656e2015-03-18 09:54:37 -070099 const PropType* type,
100 bool* value_out,
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700101 ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700102 return value_in->GetAsBoolean(value_out) ||
Alex Vakulenko7e8df462015-07-07 10:59:20 -0700103 ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700104}
105
106bool TypedValueFromJson(const base::Value* value_in,
Alex Vakulenkod94656e2015-03-18 09:54:37 -0700107 const PropType* type,
108 int* value_out,
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700109 ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700110 return value_in->GetAsInteger(value_out) ||
Alex Vakulenko7e8df462015-07-07 10:59:20 -0700111 ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700112}
113
114bool TypedValueFromJson(const base::Value* value_in,
Alex Vakulenkod94656e2015-03-18 09:54:37 -0700115 const PropType* type,
116 double* value_out,
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700117 ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700118 return value_in->GetAsDouble(value_out) ||
Alex Vakulenko7e8df462015-07-07 10:59:20 -0700119 ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700120}
121
122bool TypedValueFromJson(const base::Value* value_in,
Alex Vakulenkod94656e2015-03-18 09:54:37 -0700123 const PropType* type,
124 std::string* value_out,
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700125 ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700126 return value_in->GetAsString(value_out) ||
Alex Vakulenko7e8df462015-07-07 10:59:20 -0700127 ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700128}
129
130bool TypedValueFromJson(const base::Value* value_in,
Alex Vakulenkod94656e2015-03-18 09:54:37 -0700131 const PropType* type,
Vitaly Buka774cdf52015-07-21 13:55:00 -0700132 ValueMap* value_out,
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700133 ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700134 const base::DictionaryValue* dict = nullptr;
135 if (!value_in->GetAsDictionary(&dict))
Alex Vakulenko7e8df462015-07-07 10:59:20 -0700136 return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700137
Alex Vakulenkod94656e2015-03-18 09:54:37 -0700138 CHECK(type) << "Object definition must be provided";
139 CHECK(ValueType::Object == type->GetType()) << "Type must be Object";
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700140
Alex Vakulenkod94656e2015-03-18 09:54:37 -0700141 const ObjectSchema* object_schema = type->GetObject()->GetObjectSchemaPtr();
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700142 std::set<std::string> keys_processed;
Alex Vakulenko3e864c02015-03-24 10:19:32 -0700143 value_out->clear(); // Clear possible default values already in |value_out|.
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700144 for (const auto& pair : object_schema->GetProps()) {
145 const PropValue* def_value = pair.second->GetDefaultValue();
146 if (dict->HasKey(pair.first)) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700147 const base::Value* param_value = nullptr;
148 CHECK(dict->GetWithoutPathExpansion(pair.first, &param_value))
149 << "Unable to get parameter";
Vitaly Buka79c05e92015-07-29 12:25:37 -0700150 auto value = pair.second->CreatePropValue(*param_value, error);
151 if (!value) {
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700152 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
153 errors::commands::kInvalidPropValue,
154 "Invalid value for property '%s'",
155 pair.first.c_str());
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700156 return false;
Alex Vakulenkoa32d83a2014-09-19 15:05:24 -0700157 }
Vitaly Buka52d006a2015-11-21 17:14:51 -0800158 value_out->insert(std::make_pair(pair.first, std::move(value)));
Alex Vakulenko7e8df462015-07-07 10:59:20 -0700159 keys_processed.insert(pair.first);
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700160 } else if (def_value) {
Vitaly Buka52d006a2015-11-21 17:14:51 -0800161 value_out->insert(std::make_pair(pair.first, def_value->Clone()));
Alex Vakulenko7e8df462015-07-07 10:59:20 -0700162 keys_processed.insert(pair.first);
163 } else if (pair.second->IsRequired()) {
164 return ErrorMissingProperty(error, FROM_HERE, pair.first.c_str());
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700165 }
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700166 }
167
168 // Just for sanity, make sure that we processed all the necessary properties
169 // and there weren't any extra (unknown) ones specified. If so, ignore
170 // them, but log as warnings...
171 base::DictionaryValue::Iterator iter(*dict);
172 while (!iter.IsAtEnd()) {
173 std::string key = iter.key();
174 if (keys_processed.find(key) == keys_processed.end() &&
175 !object_schema->GetExtraPropertiesAllowed()) {
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700176 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
177 errors::commands::kUnknownProperty,
178 "Unrecognized parameter '%s'", key.c_str());
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700179 return false;
180 }
181 iter.Advance();
182 }
183
184 // Now go over all property values and validate them.
185 for (const auto& pair : *value_out) {
186 const PropType* prop_type = pair.second->GetPropType();
187 CHECK(prop_type) << "Value property type must be available";
188 if (!prop_type->ValidateConstraints(*pair.second, error)) {
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700189 Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
190 errors::commands::kInvalidPropValue,
191 "Invalid value for property '%s'", pair.first.c_str());
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700192 return false;
193 }
194 }
195 return true;
196}
197
Alex Vakulenko29e64442015-03-20 13:59:19 -0700198bool TypedValueFromJson(const base::Value* value_in,
199 const PropType* type,
Vitaly Buka774cdf52015-07-21 13:55:00 -0700200 ValueVector* value_out,
Vitaly Buka0801a1f2015-08-14 10:03:46 -0700201 ErrorPtr* error) {
Alex Vakulenko29e64442015-03-20 13:59:19 -0700202 const base::ListValue* list = nullptr;
203 if (!value_in->GetAsList(&list))
Alex Vakulenko7e8df462015-07-07 10:59:20 -0700204 return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
Alex Vakulenko29e64442015-03-20 13:59:19 -0700205
206 CHECK(type) << "Array type definition must be provided";
207 CHECK(ValueType::Array == type->GetType()) << "Type must be Array";
208 const PropType* item_type = type->GetArray()->GetItemTypePtr();
209 CHECK(item_type) << "Incomplete array type definition";
210
Alex Vakulenko3e864c02015-03-24 10:19:32 -0700211 // This value might already contain values from the type defaults.
212 // Clear them first before proceeding.
213 value_out->clear();
Alex Vakulenko29e64442015-03-20 13:59:19 -0700214 value_out->reserve(list->GetSize());
215 for (const base::Value* item : *list) {
Vitaly Buka79c05e92015-07-29 12:25:37 -0700216 std::unique_ptr<PropValue> prop_value =
217 item_type->CreatePropValue(*item, error);
218 if (!prop_value)
Alex Vakulenko29e64442015-03-20 13:59:19 -0700219 return false;
Alex Vakulenko29e64442015-03-20 13:59:19 -0700220 value_out->push_back(std::move(prop_value));
221 }
222 return true;
223}
224
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700225// Compares two sets of key-value pairs from two Objects.
Vitaly Buka774cdf52015-07-21 13:55:00 -0700226static bool obj_cmp(const ValueMap::value_type& v1,
227 const ValueMap::value_type& v2) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700228 return (v1.first == v2.first) && v1.second->IsEqual(v2.second.get());
229}
230
Vitaly Buka774cdf52015-07-21 13:55:00 -0700231bool operator==(const ValueMap& obj1, const ValueMap& obj2) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700232 if (obj1.size() != obj2.size())
233 return false;
234
235 auto pair = std::mismatch(obj1.begin(), obj1.end(), obj2.begin(), obj_cmp);
236 return pair == std::make_pair(obj1.end(), obj2.end());
237}
238
Vitaly Buka774cdf52015-07-21 13:55:00 -0700239bool operator==(const ValueVector& arr1, const ValueVector& arr2) {
Alex Vakulenko29e64442015-03-20 13:59:19 -0700240 if (arr1.size() != arr2.size())
241 return false;
242
Vitaly Buka774cdf52015-07-21 13:55:00 -0700243 using Type = const ValueVector::value_type;
Alex Vakulenko29e64442015-03-20 13:59:19 -0700244 // Compare two array items.
245 auto arr_cmp = [](const Type& v1, const Type& v2) {
246 return v1->IsEqual(v2.get());
247 };
248 auto pair = std::mismatch(arr1.begin(), arr1.end(), arr2.begin(), arr_cmp);
249 return pair == std::make_pair(arr1.end(), arr2.end());
250}
251
Vitaly Buka774cdf52015-07-21 13:55:00 -0700252std::string ToString(const ValueMap& obj) {
Vitaly Buka6942e1f2015-07-28 15:33:55 -0700253 auto val = TypedValueToJson(obj);
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700254 std::string str;
Alex Vakulenkoae1ffbc2015-06-15 12:53:22 -0700255 base::JSONWriter::Write(*val, &str);
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700256 return str;
257}
258
Vitaly Buka774cdf52015-07-21 13:55:00 -0700259std::string ToString(const ValueVector& arr) {
Vitaly Buka6942e1f2015-07-28 15:33:55 -0700260 auto val = TypedValueToJson(arr);
Alex Vakulenko29e64442015-03-20 13:59:19 -0700261 std::string str;
Alex Vakulenkoae1ffbc2015-06-15 12:53:22 -0700262 base::JSONWriter::Write(*val, &str);
Alex Vakulenko29e64442015-03-20 13:59:19 -0700263 return str;
264}
265
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700266} // namespace weave