blob: 08b00cbc49c8933422f2fbe3e95c87a842f5526e [file] [log] [blame]
Alex Vakulenko66ec2922014-06-17 15:30:22 -07001// Copyright 2014 The Chromium OS 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
5#include "buffet/commands/schema_utils.h"
6
7#include <algorithm>
8#include <set>
9#include <string>
10
11#include <base/json/json_writer.h>
12
13#include "buffet/commands/object_schema.h"
14#include "buffet/commands/prop_types.h"
15#include "buffet/commands/prop_values.h"
16
17namespace buffet {
18namespace {
19// Helper function to report "type mismatch" errors when parsing JSON.
20void ReportJsonTypeMismatch(const base::Value* value_in,
21 const std::string& expected_type,
Alex Vakulenko5f472062014-08-14 17:54:04 -070022 chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070023 std::string value_as_string;
24 base::JSONWriter::Write(value_in, &value_as_string);
Alex Vakulenko5f472062014-08-14 17:54:04 -070025 chromeos::Error::AddToPrintf(error, errors::commands::kDomain,
26 errors::commands::kTypeMismatch,
27 "Unable to convert value %s into %s",
28 value_as_string.c_str(), expected_type.c_str());
Alex Vakulenko66ec2922014-06-17 15:30:22 -070029}
30
31// Template version of ReportJsonTypeMismatch that deduces the type of expected
32// data from the value_out parameter passed to particular overload of
33// TypedValueFromJson() function. Always returns false.
34template<typename T>
Alex Vakulenko5f472062014-08-14 17:54:04 -070035bool ReportUnexpectedJson(const base::Value* value_in, T*,
36 chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070037 ReportJsonTypeMismatch(value_in,
38 PropType::GetTypeStringFromType(GetValueType<T>()),
39 error);
40 return false;
41}
42
Alex Vakulenko5f472062014-08-14 17:54:04 -070043bool ErrorMissingProperty(chromeos::ErrorPtr* error, const char* param_name) {
44 chromeos::Error::AddToPrintf(error, errors::commands::kDomain,
45 errors::commands::kPropertyMissing,
46 "Required parameter missing: %s", param_name);
Alex Vakulenko66ec2922014-06-17 15:30:22 -070047 return false;
48}
49} // namespace
50
51// Specializations of TypedValueToJson<T>() for supported C++ types.
Alex Vakulenko5f472062014-08-14 17:54:04 -070052std::unique_ptr<base::Value> TypedValueToJson(bool value,
53 chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070054 return std::unique_ptr<base::Value>(base::Value::CreateBooleanValue(value));
55}
56
Alex Vakulenko5f472062014-08-14 17:54:04 -070057std::unique_ptr<base::Value> TypedValueToJson(int value,
58 chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070059 return std::unique_ptr<base::Value>(base::Value::CreateIntegerValue(value));
60}
61
Alex Vakulenko5f472062014-08-14 17:54:04 -070062std::unique_ptr<base::Value> TypedValueToJson(double value,
63 chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070064 return std::unique_ptr<base::Value>(base::Value::CreateDoubleValue(value));
65}
66
67std::unique_ptr<base::Value> TypedValueToJson(const std::string& value,
Alex Vakulenko5f472062014-08-14 17:54:04 -070068 chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070069 return std::unique_ptr<base::Value>(base::Value::CreateStringValue(value));
70}
71
72std::unique_ptr<base::Value> TypedValueToJson(const native_types::Object& value,
Alex Vakulenko5f472062014-08-14 17:54:04 -070073 chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070074 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
75 for (const auto& pair : value) {
76 auto prop_value = pair.second->ToJson(error);
77 if (!prop_value)
78 return prop_value;
79 dict->SetWithoutPathExpansion(pair.first, prop_value.release());
80 }
81 return std::move(dict);
82}
83
84bool TypedValueFromJson(const base::Value* value_in,
85 const ObjectSchema* object_schema,
Alex Vakulenko5f472062014-08-14 17:54:04 -070086 bool* value_out, chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070087 return value_in->GetAsBoolean(value_out) ||
88 ReportUnexpectedJson(value_in, value_out, error);
89}
90
91bool TypedValueFromJson(const base::Value* value_in,
92 const ObjectSchema* object_schema,
Alex Vakulenko5f472062014-08-14 17:54:04 -070093 int* value_out, chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -070094 return value_in->GetAsInteger(value_out) ||
95 ReportUnexpectedJson(value_in, value_out, error);
96}
97
98bool TypedValueFromJson(const base::Value* value_in,
99 const ObjectSchema* object_schema,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700100 double* value_out, chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700101 return value_in->GetAsDouble(value_out) ||
102 ReportUnexpectedJson(value_in, value_out, error);
103}
104
105bool TypedValueFromJson(const base::Value* value_in,
106 const ObjectSchema* object_schema,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700107 std::string* value_out, chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700108 return value_in->GetAsString(value_out) ||
109 ReportUnexpectedJson(value_in, value_out, error);
110}
111
112bool TypedValueFromJson(const base::Value* value_in,
113 const ObjectSchema* object_schema,
Alex Vakulenko5f472062014-08-14 17:54:04 -0700114 native_types::Object* value_out,
115 chromeos::ErrorPtr* error) {
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700116 const base::DictionaryValue* dict = nullptr;
117 if (!value_in->GetAsDictionary(&dict))
118 return ReportUnexpectedJson(value_in, value_out, error);
119
120 CHECK(object_schema) << "Object schema must be provided";
121
122 std::set<std::string> keys_processed;
123 for (const auto& pair : object_schema->GetProps()) {
124 const PropValue* def_value = pair.second->GetDefaultValue();
125 if (dict->HasKey(pair.first)) {
126 std::shared_ptr<PropValue> value = pair.second->CreateValue();
127 const base::Value* param_value = nullptr;
128 CHECK(dict->GetWithoutPathExpansion(pair.first, &param_value))
129 << "Unable to get parameter";
130 if (!value->FromJson(param_value, error))
131 return false;
132 value_out->insert(std::make_pair(pair.first, std::move(value)));
133 } else if (def_value) {
134 std::shared_ptr<PropValue> value = def_value->Clone();
135 value_out->insert(std::make_pair(pair.first, std::move(value)));
136 } else {
137 return ErrorMissingProperty(error, pair.first.c_str());
138 }
139 keys_processed.insert(pair.first);
140 }
141
142 // Just for sanity, make sure that we processed all the necessary properties
143 // and there weren't any extra (unknown) ones specified. If so, ignore
144 // them, but log as warnings...
145 base::DictionaryValue::Iterator iter(*dict);
146 while (!iter.IsAtEnd()) {
147 std::string key = iter.key();
148 if (keys_processed.find(key) == keys_processed.end() &&
149 !object_schema->GetExtraPropertiesAllowed()) {
Alex Vakulenko5f472062014-08-14 17:54:04 -0700150 chromeos::Error::AddToPrintf(error, errors::commands::kDomain,
151 errors::commands::kUnknownProperty,
152 "Unrecognized parameter '%s'", key.c_str());
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700153 return false;
154 }
155 iter.Advance();
156 }
157
158 // Now go over all property values and validate them.
159 for (const auto& pair : *value_out) {
160 const PropType* prop_type = pair.second->GetPropType();
161 CHECK(prop_type) << "Value property type must be available";
162 if (!prop_type->ValidateConstraints(*pair.second, error)) {
Alex Vakulenko5f472062014-08-14 17:54:04 -0700163 chromeos::Error::AddToPrintf(error, errors::commands::kDomain,
164 errors::commands::kInvalidPropValue,
165 "Invalid parameter value for property '%s'",
166 pair.first.c_str());
Alex Vakulenko66ec2922014-06-17 15:30:22 -0700167 return false;
168 }
169 }
170 return true;
171}
172
173// Compares two sets of key-value pairs from two Objects.
174static bool obj_cmp(const native_types::Object::value_type& v1,
175 const native_types::Object::value_type& v2) {
176 return (v1.first == v2.first) && v1.second->IsEqual(v2.second.get());
177}
178
179bool operator==(const native_types::Object& obj1,
180 const native_types::Object& obj2) {
181 if (obj1.size() != obj2.size())
182 return false;
183
184 auto pair = std::mismatch(obj1.begin(), obj1.end(), obj2.begin(), obj_cmp);
185 return pair == std::make_pair(obj1.end(), obj2.end());
186}
187
188std::string ToString(const native_types::Object& obj) {
189 auto val = TypedValueToJson(obj, nullptr);
190 std::string str;
191 base::JSONWriter::Write(val.get(), &str);
192 return str;
193}
194
195
196} // namespace buffet