diff options
author | Alex Vakulenko <avakulenko@google.com> | 2015-11-24 17:13:20 -0800 |
---|---|---|
committer | Alex Vakulenko <avakulenko@google.com> | 2015-11-25 02:18:04 +0000 |
commit | 8a05bebb95f5f3f9bf6b72bd9e58b6ba6b8c9bdd (patch) | |
tree | 61cc3751a92cebe57e77dad742a131de938f5a8b /src/commands | |
parent | 42d63b9d2b41aab4cb13d55482f7ebf66fee7f27 (diff) | |
download | libweave-8a05bebb95f5f3f9bf6b72bd9e58b6ba6b8c9bdd.tar.gz |
Remove object schema type system
Since libweave no longer semantically parses command/state definitions
and validates commands and states, there is no need for this extra
code.
BUG: 25841230
Change-Id: I53dfab20db8c97b621c07fe234a7b3c6da7325bb
Reviewed-on: https://weave-review.googlesource.com/1660
Reviewed-by: Vitaly Buka <vitalybuka@google.com>
Diffstat (limited to 'src/commands')
23 files changed, 5 insertions, 4706 deletions
diff --git a/src/commands/cloud_command_proxy.cc b/src/commands/cloud_command_proxy.cc index 9ec3d3e..91db18c 100644 --- a/src/commands/cloud_command_proxy.cc +++ b/src/commands/cloud_command_proxy.cc @@ -9,8 +9,6 @@ #include <weave/provider/task_runner.h> #include "src/commands/command_instance.h" -#include "src/commands/prop_constraints.h" -#include "src/commands/prop_types.h" #include "src/commands/schema_constants.h" #include "src/utils.h" diff --git a/src/commands/cloud_command_proxy_unittest.cc b/src/commands/cloud_command_proxy_unittest.cc index fdb22fc..a65a967 100644 --- a/src/commands/cloud_command_proxy_unittest.cc +++ b/src/commands/cloud_command_proxy_unittest.cc @@ -10,10 +10,10 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> #include <weave/provider/test/fake_task_runner.h> +#include <weave/test/unittest_utils.h> #include "src/commands/command_dictionary.h" #include "src/commands/command_instance.h" -#include "src/commands/unittest_utils.h" #include "src/states/mock_state_change_queue_interface.h" using testing::_; diff --git a/src/commands/command_definition_unittest.cc b/src/commands/command_definition_unittest.cc index ecd6e54..867d48f 100644 --- a/src/commands/command_definition_unittest.cc +++ b/src/commands/command_definition_unittest.cc @@ -5,8 +5,7 @@ #include "src/commands/command_definition.h" #include <gtest/gtest.h> - -#include "src/commands/unittest_utils.h" +#include <weave/test/unittest_utils.h> namespace weave { diff --git a/src/commands/command_dictionary_unittest.cc b/src/commands/command_dictionary_unittest.cc index 7298363..adae4ec 100644 --- a/src/commands/command_dictionary_unittest.cc +++ b/src/commands/command_dictionary_unittest.cc @@ -5,8 +5,7 @@ #include "src/commands/command_dictionary.h" #include <gtest/gtest.h> - -#include "src/commands/unittest_utils.h" +#include <weave/test/unittest_utils.h> namespace weave { diff --git a/src/commands/command_instance.h b/src/commands/command_instance.h index 30ef907..e7a6eca 100644 --- a/src/commands/command_instance.h +++ b/src/commands/command_instance.h @@ -15,9 +15,6 @@ #include <weave/error.h> #include <weave/command.h> -#include "src/commands/prop_values.h" -#include "src/commands/schema_utils.h" - namespace base { class Value; } // namespace base diff --git a/src/commands/command_instance_unittest.cc b/src/commands/command_instance_unittest.cc index cacb86c..1d32cd9 100644 --- a/src/commands/command_instance_unittest.cc +++ b/src/commands/command_instance_unittest.cc @@ -5,9 +5,9 @@ #include "src/commands/command_instance.h" #include <gtest/gtest.h> +#include <weave/test/unittest_utils.h> #include "src/commands/command_dictionary.h" -#include "src/commands/unittest_utils.h" namespace weave { diff --git a/src/commands/command_manager_unittest.cc b/src/commands/command_manager_unittest.cc index 2b2b055..f0dc95d 100644 --- a/src/commands/command_manager_unittest.cc +++ b/src/commands/command_manager_unittest.cc @@ -9,9 +9,9 @@ #include <base/json/json_writer.h> #include <gtest/gtest.h> #include <weave/provider/test/mock_config_store.h> +#include <weave/test/unittest_utils.h> #include "src/bind_lambda.h" -#include "src/commands/unittest_utils.h" using testing::Return; diff --git a/src/commands/command_queue_unittest.cc b/src/commands/command_queue_unittest.cc index 394ae20..dc7290a 100644 --- a/src/commands/command_queue_unittest.cc +++ b/src/commands/command_queue_unittest.cc @@ -13,7 +13,6 @@ #include <gtest/gtest.h> #include "src/commands/command_definition.h" -#include "src/commands/object_schema.h" #include "src/string_utils.h" namespace weave { diff --git a/src/commands/object_schema.cc b/src/commands/object_schema.cc deleted file mode 100644 index 6cfa5f4..0000000 --- a/src/commands/object_schema.cc +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/commands/object_schema.h" - -#include <algorithm> -#include <limits> - -#include <base/logging.h> -#include <base/values.h> -#include <weave/enum_to_string.h> - -#include "src/commands/prop_types.h" -#include "src/commands/prop_values.h" -#include "src/commands/schema_constants.h" -#include "src/string_utils.h" - -namespace weave { - -namespace { - -// Helper function for to create a PropType based on type string. -// Generates an error if the string identifies an unknown type. -std::unique_ptr<PropType> CreatePropType(const std::string& type_name, - ErrorPtr* error) { - auto parts = SplitAtFirst(type_name, ".", false); - const std::string& primary_type = parts.first; - const std::string& array_type = parts.second; - - std::unique_ptr<PropType> prop; - ValueType type; - if (PropType::GetTypeFromTypeString(primary_type, &type)) { - prop = PropType::Create(type); - if (prop && type == ValueType::Array && !array_type.empty()) { - auto items_type = CreatePropType(array_type, error); - if (items_type) { - prop->GetArray()->SetItemType(std::move(items_type)); - } else { - prop.reset(); - return prop; - } - } - } - if (!prop) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kUnknownType, "Unknown type %s", - type_name.c_str()); - } - return prop; -} - -// Generates "no_type_info" error. -void ErrorInvalidTypeInfo(ErrorPtr* error) { - Error::AddTo(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kNoTypeInfo, - "Unable to determine parameter type"); -} - -// Helper function for PropFromJson to handle the case of parameter being -// defined as a JSON string like this: -// "prop":"..." -std::unique_ptr<PropType> PropFromJsonString(const base::Value& value, - const PropType* base_schema, - ErrorPtr* error) { - std::string type_name; - CHECK(value.GetAsString(&type_name)) << "Unable to get string value"; - std::unique_ptr<PropType> prop = CreatePropType(type_name, error); - base::DictionaryValue empty; - if (prop && !prop->FromJson(&empty, base_schema, error)) - prop.reset(); - - return prop; -} - -// Detects a type based on JSON array. Inspects the first element of the array -// to deduce the PropType from. Returns the string name of the type detected -// or empty string is type detection failed. -std::string DetectArrayType(const base::ListValue* list, - const PropType* base_schema, - bool allow_arrays) { - std::string type_name; - if (base_schema) { - type_name = base_schema->GetTypeAsString(); - } else if (list->GetSize() > 0) { - const base::Value* first_element = nullptr; - if (list->Get(0, &first_element)) { - switch (first_element->GetType()) { - case base::Value::TYPE_BOOLEAN: - type_name = PropType::GetTypeStringFromType(ValueType::Boolean); - break; - case base::Value::TYPE_INTEGER: - type_name = PropType::GetTypeStringFromType(ValueType::Int); - break; - case base::Value::TYPE_DOUBLE: - type_name = PropType::GetTypeStringFromType(ValueType::Double); - break; - case base::Value::TYPE_STRING: - type_name = PropType::GetTypeStringFromType(ValueType::String); - break; - case base::Value::TYPE_DICTIONARY: - type_name = PropType::GetTypeStringFromType(ValueType::Object); - break; - case base::Value::TYPE_LIST: { - if (allow_arrays) { - type_name = PropType::GetTypeStringFromType(ValueType::Array); - const base::ListValue* first_element_list = nullptr; - if (first_element->GetAsList(&first_element_list)) { - // We do not allow arrays of arrays. - auto child_type = - DetectArrayType(first_element_list, nullptr, false); - if (child_type.empty()) { - type_name.clear(); - } else { - type_name += '.' + child_type; - } - } - } - break; - } - default: - // The rest are unsupported. - break; - } - } - } - return type_name; -} - -// Helper function for PropFromJson to handle the case of parameter being -// defined as a JSON array like this: -// "prop":[...] -std::unique_ptr<PropType> PropFromJsonArray(const base::Value& value, - const PropType* base_schema, - ErrorPtr* error) { - std::unique_ptr<PropType> prop; - const base::ListValue* list = nullptr; - CHECK(value.GetAsList(&list)) << "Unable to get array value"; - std::string type_name = DetectArrayType(list, base_schema, true); - if (type_name.empty()) { - ErrorInvalidTypeInfo(error); - return prop; - } - base::DictionaryValue array_object; - array_object.SetWithoutPathExpansion(commands::attributes::kOneOf_Enum, - list->DeepCopy()); - prop = CreatePropType(type_name, error); - if (prop && !prop->FromJson(&array_object, base_schema, error)) - prop.reset(); - - return prop; -} - -// Detects a type based on JSON object definition of type. Looks at various -// members such as minimum/maximum constraints, default and enum values to -// try to deduce the underlying type of the element. Returns the string name of -// the type detected or empty string is type detection failed. -std::string DetectObjectType(const base::DictionaryValue* dict, - const PropType* base_schema) { - bool has_min_max = dict->HasKey(commands::attributes::kNumeric_Min) || - dict->HasKey(commands::attributes::kNumeric_Max); - - // Here we are trying to "detect the type and read in the object based on - // the deduced type". Later, we'll verify that this detected type matches - // the expectation of the base schema, if applicable, to make sure we are not - // changing the expected type. This makes the vendor-side (re)definition of - // standard and custom commands behave exactly the same. - // The only problem with this approach was the double-vs-int types. - // If the type is meant to be a double we want to allow its definition as - // "min:0, max:0" instead of just forcing it to be only "min:0.0, max:0.0". - // If we have "minimum" or "maximum", and we have a Double schema object, - // treat this object as a Double (even if both min and max are integers). - if (has_min_max && base_schema && base_schema->GetType() == ValueType::Double) - return PropType::GetTypeStringFromType(ValueType::Double); - - // If we have at least one "minimum" or "maximum" that is Double, - // it's a Double. - const base::Value* value = nullptr; - if (dict->Get(commands::attributes::kNumeric_Min, &value) && - value->IsType(base::Value::TYPE_DOUBLE)) - return PropType::GetTypeStringFromType(ValueType::Double); - if (dict->Get(commands::attributes::kNumeric_Max, &value) && - value->IsType(base::Value::TYPE_DOUBLE)) - return PropType::GetTypeStringFromType(ValueType::Double); - - // If we have "minimum" or "maximum", it's an Integer. - if (has_min_max) - return PropType::GetTypeStringFromType(ValueType::Int); - - // If we have "minLength" or "maxLength", it's a String. - if (dict->HasKey(commands::attributes::kString_MinLength) || - dict->HasKey(commands::attributes::kString_MaxLength)) - return PropType::GetTypeStringFromType(ValueType::String); - - // If we have "properties", it's an object. - if (dict->HasKey(commands::attributes::kObject_Properties)) - return PropType::GetTypeStringFromType(ValueType::Object); - - // If we have "items", it's an array. - if (dict->HasKey(commands::attributes::kItems)) - return PropType::GetTypeStringFromType(ValueType::Array); - - // If we have "enum", it's an array. Detect type from array elements. - const base::ListValue* list = nullptr; - if (dict->GetListWithoutPathExpansion(commands::attributes::kOneOf_Enum, - &list)) - return DetectArrayType(list, base_schema, true); - - // If we have "default", try to use it for type detection. - if (dict->Get(commands::attributes::kDefault, &value)) { - if (value->IsType(base::Value::TYPE_DOUBLE)) - return PropType::GetTypeStringFromType(ValueType::Double); - if (value->IsType(base::Value::TYPE_INTEGER)) - return PropType::GetTypeStringFromType(ValueType::Int); - if (value->IsType(base::Value::TYPE_BOOLEAN)) - return PropType::GetTypeStringFromType(ValueType::Boolean); - if (value->IsType(base::Value::TYPE_STRING)) - return PropType::GetTypeStringFromType(ValueType::String); - if (value->IsType(base::Value::TYPE_LIST)) { - CHECK(value->GetAsList(&list)) << "List value expected"; - std::string child_type = DetectArrayType(list, base_schema, false); - if (!child_type.empty()) { - return PropType::GetTypeStringFromType(ValueType::Array) + '.' + - child_type; - } - } - } - - return std::string{}; -} - -// Helper function for PropFromJson to handle the case of parameter being -// defined as a JSON object like this: -// "prop":{...} -std::unique_ptr<PropType> PropFromJsonObject(const base::Value& value, - const PropType* base_schema, - ErrorPtr* error) { - std::unique_ptr<PropType> prop; - const base::DictionaryValue* dict = nullptr; - CHECK(value.GetAsDictionary(&dict)) << "Unable to get dictionary value"; - std::string type_name; - if (dict->HasKey(commands::attributes::kType)) { - if (!dict->GetString(commands::attributes::kType, &type_name)) { - ErrorInvalidTypeInfo(error); - return prop; - } - } else { - type_name = DetectObjectType(dict, base_schema); - } - if (type_name.empty()) { - if (!base_schema) { - ErrorInvalidTypeInfo(error); - return prop; - } - type_name = base_schema->GetTypeAsString(); - } - prop = CreatePropType(type_name, error); - if (prop && !prop->FromJson(dict, base_schema, error)) - prop.reset(); - - return prop; -} - -const EnumToStringMap<base::Value::Type>::Map kMap[] = { - {base::Value::TYPE_NULL, "Null"}, - {base::Value::TYPE_BOOLEAN, "Boolean"}, - {base::Value::TYPE_INTEGER, "Integer"}, - {base::Value::TYPE_DOUBLE, "Double"}, - {base::Value::TYPE_STRING, "String"}, - {base::Value::TYPE_BINARY, "Binary"}, - {base::Value::TYPE_DICTIONARY, "Object"}, - {base::Value::TYPE_LIST, "Array"}, -}; - -} // anonymous namespace - -template <> -EnumToStringMap<base::Value::Type>::EnumToStringMap() : EnumToStringMap(kMap) {} - -ObjectSchema::ObjectSchema() {} -ObjectSchema::~ObjectSchema() {} - -std::unique_ptr<ObjectSchema> ObjectSchema::Clone() const { - std::unique_ptr<ObjectSchema> cloned{new ObjectSchema}; - for (const auto& pair : properties_) { - cloned->properties_.insert( - std::make_pair(pair.first, pair.second->Clone())); - } - cloned->extra_properties_allowed_ = extra_properties_allowed_; - return cloned; -} - -void ObjectSchema::AddProp(const std::string& name, - std::unique_ptr<PropType> prop) { - // Not using emplace() here to make sure we override existing properties. - properties_[name] = std::move(prop); -} - -const PropType* ObjectSchema::GetProp(const std::string& name) const { - auto p = properties_.find(name); - return p != properties_.end() ? p->second.get() : nullptr; -} - -bool ObjectSchema::MarkPropRequired(const std::string& name, ErrorPtr* error) { - auto p = properties_.find(name); - if (p == properties_.end()) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kUnknownProperty, - "Unknown property '%s'", name.c_str()); - return false; - } - p->second->MakeRequired(true); - return true; -} - -std::unique_ptr<base::DictionaryValue> ObjectSchema::ToJson( - bool full_schema, - bool in_command_def) const { - std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue); - for (const auto& pair : properties_) { - auto prop_def = pair.second->ToJson(full_schema, in_command_def); - CHECK(prop_def); - value->SetWithoutPathExpansion(pair.first, prop_def.release()); - } - return value; -} - -bool ObjectSchema::FromJson(const base::DictionaryValue* value, - const ObjectSchema* object_schema, - ErrorPtr* error) { - Properties properties; - base::DictionaryValue::Iterator iter(*value); - while (!iter.IsAtEnd()) { - std::string name = iter.key(); - const PropType* base_schema = - object_schema ? object_schema->GetProp(iter.key()) : nullptr; - auto prop_type = PropFromJson(iter.value(), base_schema, error); - if (prop_type) { - properties.insert(std::make_pair(iter.key(), std::move(prop_type))); - } else { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kInvalidPropDef, - "Error in definition of property '%s'", - iter.key().c_str()); - return false; - } - iter.Advance(); - } - properties_ = std::move(properties); - return true; -} - -std::unique_ptr<PropType> ObjectSchema::PropFromJson( - const base::Value& value, - const PropType* base_schema, - ErrorPtr* error) { - if (value.IsType(base::Value::TYPE_STRING)) { - // A string value is a short-hand object specification and provides - // the parameter type. - return PropFromJsonString(value, base_schema, error); - } else if (value.IsType(base::Value::TYPE_LIST)) { - // One of the enumerated types. - return PropFromJsonArray(value, base_schema, error); - } else if (value.IsType(base::Value::TYPE_DICTIONARY)) { - // Full parameter definition. - return PropFromJsonObject(value, base_schema, error); - } - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kUnknownType, - "Unexpected JSON value type: %s", - EnumToString(value.GetType()).c_str()); - return nullptr; -} - -std::unique_ptr<ObjectSchema> ObjectSchema::Create() { - return std::unique_ptr<ObjectSchema>{new ObjectSchema}; -} - -} // namespace weave diff --git a/src/commands/object_schema.h b/src/commands/object_schema.h deleted file mode 100644 index 504e1dd..0000000 --- a/src/commands/object_schema.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef LIBWEAVE_SRC_COMMANDS_OBJECT_SCHEMA_H_ -#define LIBWEAVE_SRC_COMMANDS_OBJECT_SCHEMA_H_ - -#include <map> -#include <memory> -#include <string> - -#include <weave/error.h> - -namespace base { -class Value; -class DictionaryValue; -} // namespace base - -namespace weave { - -class PropType; - -// ObjectSchema is a class representing an object definition in GCD command -// schema. This could represent a GCD command definition, but also it can be -// used when defining custom object types for command properties such as -// output media type (paper) for print command. The schema definition for -// these type of object description is the same. -class ObjectSchema final { - public: - // Do not inline the constructor/destructor to allow forward-declared type - // PropType to be part of |properties_| member. - ObjectSchema(); - ~ObjectSchema(); - - // Properties is a string-to-PropType map representing a list of - // properties defined for a command/object. The key is the parameter - // name and the value is the parameter type definition object. - using Properties = std::map<std::string, std::unique_ptr<PropType>>; - - // Makes a full copy of this object. - virtual std::unique_ptr<ObjectSchema> Clone() const; - - // Add a new parameter definition. - void AddProp(const std::string& name, std::unique_ptr<PropType> prop); - - // Finds parameter type definition by name. Returns nullptr if not found. - const PropType* GetProp(const std::string& name) const; - - // Gets the list of all the properties defined. - const Properties& GetProps() const { return properties_; } - - // Marks the property with given name as "required". If |name| specifies - // an unknown property, false is returned and |error| is set with detailed - // error message for the failure. - bool MarkPropRequired(const std::string& name, ErrorPtr* error); - - // Specify whether extra properties are allowed on objects described by - // this schema. When validating a value of an object type, we can - // make sure that the value has only the properties explicitly defined by - // the schema and no other (custom) properties are allowed. - // This is to support JSON Schema's "additionalProperties" specification. - bool GetExtraPropertiesAllowed() const { return extra_properties_allowed_; } - void SetExtraPropertiesAllowed(bool allowed) { - extra_properties_allowed_ = allowed; - } - - // Saves the object schema to JSON. When |full_schema| is set to true, - // then all properties and constraints are saved, otherwise, only - // the overridden (not inherited) ones are saved. - std::unique_ptr<base::DictionaryValue> ToJson(bool full_schema, - bool in_command_def) const; - - // Loads the object schema from JSON. If |object_schema| is not nullptr, it is - // used as a base schema to inherit omitted properties and constraints from. - bool FromJson(const base::DictionaryValue* value, - const ObjectSchema* object_schema, - ErrorPtr* error); - - // Helper factory method to create a new instance of ObjectSchema object. - static std::unique_ptr<ObjectSchema> Create(); - - // Helper method to load property type definitions from JSON. - static std::unique_ptr<PropType> PropFromJson(const base::Value& value, - const PropType* base_schema, - ErrorPtr* error); - - private: - // Internal parameter type definition map. - Properties properties_; - bool extra_properties_allowed_{false}; -}; - -} // namespace weave - -#endif // LIBWEAVE_SRC_COMMANDS_OBJECT_SCHEMA_H_ diff --git a/src/commands/object_schema_unittest.cc b/src/commands/object_schema_unittest.cc deleted file mode 100644 index 7cdc16d..0000000 --- a/src/commands/object_schema_unittest.cc +++ /dev/null @@ -1,1697 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/commands/object_schema.h" - -#include <algorithm> -#include <limits> -#include <memory> -#include <vector> - -#include <base/json/json_reader.h> -#include <base/json/json_writer.h> -#include <base/values.h> -#include <gtest/gtest.h> - -#include "src/commands/prop_constraints.h" -#include "src/commands/prop_types.h" -#include "src/commands/schema_constants.h" -#include "src/commands/unittest_utils.h" - -namespace weave { - -using test::CreateValue; -using test::CreateDictionaryValue; - -namespace { - -template <typename T> -std::vector<T> GetArrayValues(const ValueVector& arr) { - std::vector<T> values; - values.reserve(arr.size()); - for (const auto& prop_value : arr) { - const auto& value = static_cast<const TypedValueBase<T>&>(*prop_value); - values.push_back(value.GetValue()); - } - return values; -} - -template <typename T> -std::vector<T> GetOneOfValues(const PropType* prop_type) { - auto one_of = static_cast<const ConstraintOneOf*>( - prop_type->GetConstraint(ConstraintType::OneOf)); - if (!one_of) - return {}; - - return GetArrayValues<T>(one_of->set_.value); -} - -bool ValidateValue(const PropType& type, - const base::Value& value, - ErrorPtr* error) { - std::unique_ptr<PropValue> val = type.CreatePropValue(value, error); - return val != nullptr; -} - -} // anonymous namespace - -TEST(CommandSchema, IntPropType_Empty) { - IntPropType prop; - EXPECT_TRUE(prop.GetConstraints().empty()); - EXPECT_FALSE(prop.HasOverriddenAttributes()); - EXPECT_FALSE(prop.IsBasedOnSchema()); - EXPECT_EQ(nullptr, prop.GetDefaultValue()); -} - -TEST(CommandSchema, IntPropType_Types) { - IntPropType prop; - EXPECT_EQ(nullptr, prop.GetBoolean()); - EXPECT_EQ(&prop, prop.GetInt()); - EXPECT_EQ(nullptr, prop.GetDouble()); - EXPECT_EQ(nullptr, prop.GetString()); - EXPECT_EQ(nullptr, prop.GetObject()); - EXPECT_EQ(nullptr, prop.GetArray()); -} - -TEST(CommandSchema, IntPropType_ToJson) { - IntPropType prop; - EXPECT_JSON_EQ("'integer'", *prop.ToJson(false, false)); - EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false)); - IntPropType param2; - param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); - EXPECT_JSON_EQ("{}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'minimum':3}").get(), &prop, nullptr); - EXPECT_JSON_EQ("{'minimum':3}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'maximum':-7}").get(), &prop, - nullptr); - EXPECT_JSON_EQ("{'maximum':-7}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':5}").get(), - &prop, nullptr); - EXPECT_JSON_EQ("{'maximum':5,'minimum':0}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'enum':[1,2,3]}").get(), &prop, - nullptr); - EXPECT_JSON_EQ("[1,2,3]", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'default':123}").get(), &prop, - nullptr); - EXPECT_JSON_EQ("{'default':123}", *param2.ToJson(false, false)); -} - -TEST(CommandSchema, IntPropType_FromJson) { - IntPropType prop; - prop.AddMinMaxConstraint(2, 8); - IntPropType param2; - param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); - EXPECT_FALSE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_EQ(2, prop.GetMinValue()); - EXPECT_EQ(8, prop.GetMaxValue()); - prop.AddMinMaxConstraint(-2, 30); - param2.FromJson(CreateDictionaryValue("{'minimum':7}").get(), &prop, nullptr); - EXPECT_TRUE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_EQ(7, param2.GetMinValue()); - EXPECT_EQ(30, param2.GetMaxValue()); - param2.FromJson(CreateDictionaryValue("{'maximum':17}").get(), &prop, - nullptr); - EXPECT_TRUE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_EQ(-2, param2.GetMinValue()); - EXPECT_EQ(17, param2.GetMaxValue()); - - ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':3}").get(), - &prop, nullptr)); - EXPECT_TRUE(param2.HasOverriddenAttributes()); - ASSERT_NE(nullptr, param2.GetDefaultValue()); - EXPECT_EQ(3, param2.GetDefaultValue()->GetInt()->GetValue()); -} - -TEST(CommandSchema, IntPropType_Validate) { - IntPropType prop; - prop.AddMinMaxConstraint(2, 4); - ErrorPtr error; - EXPECT_FALSE(ValidateValue(prop, *CreateValue("-1"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("0"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("1"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("2"), &error)); - EXPECT_EQ(nullptr, error.get()); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("3"), &error)); - EXPECT_EQ(nullptr, error.get()); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("4"), &error)); - EXPECT_EQ(nullptr, error.get()); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("5"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("true"), &error)); - EXPECT_EQ("type_mismatch", error->GetCode()); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("3.0"), &error)); - EXPECT_EQ("type_mismatch", error->GetCode()); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("'3'"), &error)); - EXPECT_EQ("type_mismatch", error->GetCode()); -} - -TEST(CommandSchema, IntPropType_CreateValue) { - IntPropType prop; - ErrorPtr error; - auto val = prop.CreateValue(base::FundamentalValue{2}, &error); - ASSERT_NE(nullptr, val.get()); - EXPECT_EQ(nullptr, error.get()); - EXPECT_EQ(2, val->GetValue()); - - val = prop.CreateValue(base::StringValue{"blah"}, &error); - EXPECT_EQ(nullptr, val.get()); - ASSERT_NE(nullptr, error.get()); - EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); -} - -/////////////////////////////////////////////////////////////////////////////// - -TEST(CommandSchema, BoolPropType_Empty) { - BooleanPropType prop; - EXPECT_TRUE(prop.GetConstraints().empty()); - EXPECT_FALSE(prop.HasOverriddenAttributes()); - EXPECT_FALSE(prop.IsBasedOnSchema()); - EXPECT_EQ(nullptr, prop.GetDefaultValue()); - EXPECT_FALSE(prop.IsRequired()); -} - -TEST(CommandSchema, BoolPropType_Types) { - BooleanPropType prop; - EXPECT_EQ(nullptr, prop.GetInt()); - EXPECT_EQ(&prop, prop.GetBoolean()); - EXPECT_EQ(nullptr, prop.GetDouble()); - EXPECT_EQ(nullptr, prop.GetString()); - EXPECT_EQ(nullptr, prop.GetObject()); - EXPECT_EQ(nullptr, prop.GetArray()); -} - -TEST(CommandSchema, BoolPropType_ToJson) { - BooleanPropType prop; - EXPECT_JSON_EQ("'boolean'", *prop.ToJson(false, false)); - EXPECT_JSON_EQ("{'type':'boolean'}", *prop.ToJson(true, false)); - BooleanPropType param2; - param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); - EXPECT_JSON_EQ("{}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'enum':[true,false]}").get(), &prop, - nullptr); - EXPECT_JSON_EQ("[true,false]", *param2.ToJson(false, false)); - EXPECT_JSON_EQ("{'enum':[true,false],'type':'boolean'}", - *param2.ToJson(true, false)); - param2.FromJson(CreateDictionaryValue("{'default':true}").get(), &prop, - nullptr); - EXPECT_JSON_EQ("{'default':true}", *param2.ToJson(false, false)); -} - -TEST(CommandSchema, BoolPropType_FromJson) { - BooleanPropType prop; - prop.FromJson(CreateDictionaryValue("{'enum':[true]}").get(), &prop, nullptr); - BooleanPropType param2; - param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); - EXPECT_FALSE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_EQ(std::vector<bool>{true}, GetOneOfValues<bool>(&prop)); - - BooleanPropType prop_base; - BooleanPropType param3; - ASSERT_TRUE(param3.FromJson(CreateDictionaryValue("{'default':false}").get(), - &prop_base, nullptr)); - EXPECT_TRUE(param3.HasOverriddenAttributes()); - ASSERT_NE(nullptr, param3.GetDefaultValue()); - EXPECT_FALSE(param3.GetDefaultValue()->GetBoolean()->GetValue()); -} - -TEST(CommandSchema, BoolPropType_Validate) { - BooleanPropType prop; - prop.FromJson(CreateDictionaryValue("{'enum':[true]}").get(), &prop, nullptr); - ErrorPtr error; - EXPECT_FALSE(ValidateValue(prop, *CreateValue("false"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("true"), &error)); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("1"), &error)); - EXPECT_EQ("type_mismatch", error->GetCode()); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("3.0"), &error)); - EXPECT_EQ("type_mismatch", error->GetCode()); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("'3'"), &error)); - EXPECT_EQ("type_mismatch", error->GetCode()); -} - -TEST(CommandSchema, BoolPropType_CreateValue) { - BooleanPropType prop; - ErrorPtr error; - auto val = prop.CreateValue(base::FundamentalValue{true}, &error); - ASSERT_NE(nullptr, val.get()); - EXPECT_EQ(nullptr, error.get()); - EXPECT_TRUE(val->GetValue()); - - val = prop.CreateValue(base::StringValue{"blah"}, &error); - EXPECT_EQ(nullptr, val.get()); - ASSERT_NE(nullptr, error.get()); - EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); -} - -/////////////////////////////////////////////////////////////////////////////// - -TEST(CommandSchema, DoublePropType_Empty) { - DoublePropType prop; - EXPECT_DOUBLE_EQ(std::numeric_limits<double>::lowest(), prop.GetMinValue()); - EXPECT_DOUBLE_EQ((std::numeric_limits<double>::max)(), prop.GetMaxValue()); - EXPECT_FALSE(prop.HasOverriddenAttributes()); - EXPECT_FALSE(prop.IsBasedOnSchema()); - EXPECT_EQ(nullptr, prop.GetDefaultValue()); - EXPECT_FALSE(prop.IsRequired()); -} - -TEST(CommandSchema, DoublePropType_Types) { - DoublePropType prop; - EXPECT_EQ(nullptr, prop.GetInt()); - EXPECT_EQ(nullptr, prop.GetBoolean()); - EXPECT_EQ(&prop, prop.GetDouble()); - EXPECT_EQ(nullptr, prop.GetString()); - EXPECT_EQ(nullptr, prop.GetObject()); - EXPECT_EQ(nullptr, prop.GetArray()); -} - -TEST(CommandSchema, DoublePropType_ToJson) { - DoublePropType prop; - EXPECT_JSON_EQ("'number'", *prop.ToJson(false, false)); - EXPECT_JSON_EQ("{'type':'number'}", *prop.ToJson(true, false)); - DoublePropType param2; - param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); - EXPECT_JSON_EQ("{}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'minimum':3}").get(), &prop, nullptr); - EXPECT_JSON_EQ("{'minimum':3.0}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'maximum':-7}").get(), &prop, - nullptr); - EXPECT_JSON_EQ("{'maximum':-7.0}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':5}").get(), - &prop, nullptr); - EXPECT_JSON_EQ("{'maximum':5.0,'minimum':0.0}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'default':12.3}").get(), &prop, - nullptr); - EXPECT_JSON_EQ("{'default':12.3}", *param2.ToJson(false, false)); -} - -TEST(CommandSchema, DoublePropType_FromJson) { - DoublePropType prop; - prop.AddMinMaxConstraint(2.5, 8.7); - DoublePropType param2; - param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); - EXPECT_FALSE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_DOUBLE_EQ(2.5, prop.GetMinValue()); - EXPECT_DOUBLE_EQ(8.7, prop.GetMaxValue()); - prop.AddMinMaxConstraint(-2.2, 30.4); - param2.FromJson(CreateDictionaryValue("{'minimum':7}").get(), &prop, nullptr); - EXPECT_TRUE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_DOUBLE_EQ(7.0, param2.GetMinValue()); - EXPECT_DOUBLE_EQ(30.4, param2.GetMaxValue()); - param2.FromJson(CreateDictionaryValue("{'maximum':17.2}").get(), &prop, - nullptr); - EXPECT_TRUE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_DOUBLE_EQ(-2.2, param2.GetMinValue()); - EXPECT_DOUBLE_EQ(17.2, param2.GetMaxValue()); - param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':6.1}").get(), - &prop, nullptr); - EXPECT_TRUE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_DOUBLE_EQ(0.0, param2.GetMinValue()); - EXPECT_DOUBLE_EQ(6.1, param2.GetMaxValue()); - - ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':-1.234}").get(), - &prop, nullptr)); - EXPECT_TRUE(param2.HasOverriddenAttributes()); - ASSERT_NE(nullptr, param2.GetDefaultValue()); - EXPECT_DOUBLE_EQ(-1.234, param2.GetDefaultValue()->GetDouble()->GetValue()); -} - -TEST(CommandSchema, DoublePropType_Validate) { - DoublePropType prop; - prop.AddMinMaxConstraint(-1.2, 1.3); - ErrorPtr error; - EXPECT_FALSE(ValidateValue(prop, *CreateValue("-2"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("-1.3"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("-1.2"), &error)); - EXPECT_EQ(nullptr, error.get()); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("0.0"), &error)); - EXPECT_EQ(nullptr, error.get()); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("1.3"), &error)); - EXPECT_EQ(nullptr, error.get()); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("1.31"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("true"), &error)); - EXPECT_EQ("type_mismatch", error->GetCode()); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("'0.0'"), &error)); - EXPECT_EQ("type_mismatch", error->GetCode()); -} - -TEST(CommandSchema, DoublePropType_CreateValue) { - DoublePropType prop; - ErrorPtr error; - auto val = prop.CreateValue(base::FundamentalValue{2.0}, &error); - ASSERT_NE(nullptr, val.get()); - EXPECT_EQ(nullptr, error.get()); - EXPECT_DOUBLE_EQ(2.0, val->GetValue()); - - val = prop.CreateValue(base::StringValue{"blah"}, &error); - EXPECT_EQ(nullptr, val.get()); - ASSERT_NE(nullptr, error.get()); - EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); -} - -/////////////////////////////////////////////////////////////////////////////// - -TEST(CommandSchema, StringPropType_Empty) { - StringPropType prop; - EXPECT_EQ(0, prop.GetMinLength()); - EXPECT_EQ((std::numeric_limits<int>::max)(), prop.GetMaxLength()); - EXPECT_FALSE(prop.HasOverriddenAttributes()); - EXPECT_FALSE(prop.IsBasedOnSchema()); - EXPECT_EQ(nullptr, prop.GetDefaultValue()); - EXPECT_FALSE(prop.IsRequired()); -} - -TEST(CommandSchema, StringPropType_Types) { - StringPropType prop; - EXPECT_EQ(nullptr, prop.GetInt()); - EXPECT_EQ(nullptr, prop.GetBoolean()); - EXPECT_EQ(nullptr, prop.GetDouble()); - EXPECT_EQ(&prop, prop.GetString()); - EXPECT_EQ(nullptr, prop.GetObject()); - EXPECT_EQ(nullptr, prop.GetArray()); -} - -TEST(CommandSchema, StringPropType_ToJson) { - StringPropType prop; - EXPECT_JSON_EQ("'string'", *prop.ToJson(false, false)); - EXPECT_JSON_EQ("{'type':'string'}", *prop.ToJson(true, false)); - StringPropType param2; - param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); - EXPECT_JSON_EQ("{}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'minLength':3}").get(), &prop, - nullptr); - EXPECT_JSON_EQ("{'minLength':3}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'maxLength':7}").get(), &prop, - nullptr); - EXPECT_JSON_EQ("{'maxLength':7}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'minLength':0,'maxLength':5}").get(), - &prop, nullptr); - EXPECT_JSON_EQ("{'maxLength':5,'minLength':0}", *param2.ToJson(false, false)); - param2.FromJson(CreateDictionaryValue("{'default':'abcd'}").get(), &prop, - nullptr); - EXPECT_JSON_EQ("{'default':'abcd'}", *param2.ToJson(false, false)); -} - -TEST(CommandSchema, StringPropType_FromJson) { - StringPropType prop; - prop.AddLengthConstraint(2, 8); - StringPropType param2; - param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); - EXPECT_FALSE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_EQ(2, prop.GetMinLength()); - EXPECT_EQ(8, prop.GetMaxLength()); - prop.AddLengthConstraint(3, 5); - param2.FromJson(CreateDictionaryValue("{'minLength':4}").get(), &prop, - nullptr); - EXPECT_TRUE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_EQ(4, param2.GetMinLength()); - EXPECT_EQ(5, param2.GetMaxLength()); - param2.FromJson(CreateDictionaryValue("{'maxLength':8}").get(), &prop, - nullptr); - EXPECT_TRUE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_EQ(3, param2.GetMinLength()); - EXPECT_EQ(8, param2.GetMaxLength()); - param2.FromJson(CreateDictionaryValue("{'minLength':1,'maxLength':7}").get(), - &prop, nullptr); - EXPECT_TRUE(param2.HasOverriddenAttributes()); - EXPECT_TRUE(param2.IsBasedOnSchema()); - EXPECT_EQ(1, param2.GetMinLength()); - EXPECT_EQ(7, param2.GetMaxLength()); - - ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':'foo'}").get(), - &prop, nullptr)); - EXPECT_TRUE(param2.HasOverriddenAttributes()); - ASSERT_NE(nullptr, param2.GetDefaultValue()); - EXPECT_EQ("foo", param2.GetDefaultValue()->GetString()->GetValue()); -} - -TEST(CommandSchema, StringPropType_Validate) { - StringPropType prop; - prop.AddLengthConstraint(1, 3); - ErrorPtr error; - EXPECT_FALSE(ValidateValue(prop, *CreateValue("''"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - prop.AddLengthConstraint(2, 3); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("''"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("'a'"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("'ab'"), &error)); - EXPECT_EQ(nullptr, error.get()); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("'abc'"), &error)); - EXPECT_EQ(nullptr, error.get()); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("'abcd'"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - - prop.FromJson(CreateDictionaryValue("{'enum':['abc','def','xyz!!']}").get(), - nullptr, &error); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("'abc'"), &error)); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("'def'"), &error)); - EXPECT_TRUE(ValidateValue(prop, *CreateValue("'xyz!!'"), &error)); - EXPECT_FALSE(ValidateValue(prop, *CreateValue("'xyz'"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); -} - -TEST(CommandSchema, StringPropType_CreateValue) { - StringPropType prop; - ErrorPtr error; - auto val = prop.CreateValue(base::StringValue{"blah"}, &error); - ASSERT_NE(nullptr, val.get()); - EXPECT_EQ(nullptr, error.get()); - EXPECT_EQ("blah", val->GetValue()); - - val = prop.CreateValue(base::FundamentalValue{4}, &error); - EXPECT_EQ(nullptr, val.get()); - ASSERT_NE(nullptr, error.get()); - EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); -} - -/////////////////////////////////////////////////////////////////////////////// - -TEST(CommandSchema, ObjectPropType_Empty) { - ObjectPropType prop; - EXPECT_TRUE(prop.HasOverriddenAttributes()); - EXPECT_FALSE(prop.IsBasedOnSchema()); - EXPECT_EQ(nullptr, prop.GetDefaultValue()); - EXPECT_FALSE(prop.IsRequired()); -} - -TEST(CommandSchema, ObjectPropType_Types) { - ObjectPropType prop; - EXPECT_EQ(nullptr, prop.GetInt()); - EXPECT_EQ(nullptr, prop.GetBoolean()); - EXPECT_EQ(nullptr, prop.GetDouble()); - EXPECT_EQ(nullptr, prop.GetString()); - EXPECT_EQ(&prop, prop.GetObject()); - EXPECT_EQ(nullptr, prop.GetArray()); -} - -TEST(CommandSchema, ObjectPropType_ToJson) { - ObjectPropType prop; - EXPECT_JSON_EQ("{'additionalProperties':false,'properties':{}}", - *prop.ToJson(false, false)); - EXPECT_JSON_EQ( - "{'additionalProperties':false,'properties':{},'type':'object'}", - *prop.ToJson(true, false)); - EXPECT_FALSE(prop.IsBasedOnSchema()); - ObjectPropType prop2; - prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); - EXPECT_JSON_EQ("{}", *prop2.ToJson(false, false)); - EXPECT_TRUE(prop2.IsBasedOnSchema()); - - auto schema = ObjectSchema::Create(); - schema->AddProp("expires", PropType::Create(ValueType::Int)); - auto pw = PropType::Create(ValueType::String); - pw->GetString()->AddLengthConstraint(6, 100); - schema->AddProp("password", std::move(pw)); - prop2.SetObjectSchema(std::move(schema)); - auto expected = R"({ - 'additionalProperties': false, - 'properties': { - 'expires': 'integer', - 'password': { - 'maxLength': 100, - 'minLength': 6 - } - } - })"; - EXPECT_JSON_EQ(expected, *prop2.ToJson(false, false)); - - expected = R"({ - 'additionalProperties': false, - 'properties': { - 'expires': { - 'type': 'integer' - }, - 'password': { - 'maxLength': 100, - 'minLength': 6, - 'type': 'string' - } - }, - 'type': 'object' - })"; - EXPECT_JSON_EQ(expected, *prop2.ToJson(true, false)); - - ObjectPropType prop3; - ASSERT_TRUE( - prop3.FromJson(CreateDictionaryValue( - "{'default':{'expires':3,'password':'abracadabra'}}") - .get(), - &prop2, nullptr)); - expected = R"({ - 'default': { - 'expires': 3, - 'password': 'abracadabra' - } - })"; - EXPECT_JSON_EQ(expected, *prop3.ToJson(false, false)); - - expected = R"({ - 'additionalProperties': false, - 'default': { - 'expires': 3, - 'password': 'abracadabra' - }, - 'properties': { - 'expires': { - 'type': 'integer' - }, - 'password': { - 'maxLength': 100, - 'minLength': 6, - 'type': 'string' - } - }, - 'type': 'object' - })"; - EXPECT_JSON_EQ(expected, *prop3.ToJson(true, false)); - - ObjectPropType prop4; - ASSERT_TRUE(prop4.FromJson( - CreateDictionaryValue("{'additionalProperties':true," - "'default':{'expires':3,'password':'abracadabra'}}") - .get(), - &prop2, nullptr)); - expected = R"({ - 'additionalProperties': true, - 'default': { - 'expires': 3, - 'password': 'abracadabra' - }, - 'properties': { - 'expires': 'integer', - 'password': { - 'maxLength': 100, - 'minLength': 6 - } - } - })"; - EXPECT_JSON_EQ(expected, *prop4.ToJson(false, false)); - - expected = R"({ - 'additionalProperties': true, - 'default': { - 'expires': 3, - 'password': 'abracadabra' - }, - 'properties': { - 'expires': { - 'type': 'integer' - }, - 'password': { - 'maxLength': 100, - 'minLength': 6, - 'type': 'string' - } - }, - 'type': 'object' - })"; - EXPECT_JSON_EQ(expected, *prop4.ToJson(true, false)); -} - -TEST(CommandSchema, ObjectPropType_FromJson) { - ObjectPropType base_prop; - EXPECT_TRUE(base_prop.FromJson( - CreateDictionaryValue("{'properties':{'name':'string','age':'integer'}}") - .get(), - nullptr, nullptr)); - auto schema = base_prop.GetObject()->GetObjectSchemaPtr(); - const PropType* prop = schema->GetProp("name"); - EXPECT_EQ(ValueType::String, prop->GetType()); - prop = schema->GetProp("age"); - EXPECT_EQ(ValueType::Int, prop->GetType()); - - ObjectPropType prop2; - ASSERT_TRUE(prop2.FromJson( - CreateDictionaryValue("{'properties':{'name':'string','age':'integer'}," - "'default':{'name':'Bob','age':33}}") - .get(), - nullptr, nullptr)); - ASSERT_NE(nullptr, prop2.GetDefaultValue()); - const ObjectValue* defval = prop2.GetDefaultValue()->GetObject(); - ASSERT_NE(nullptr, defval); - ValueMap objval = defval->GetValue(); - EXPECT_EQ("Bob", objval["name"]->GetString()->GetValue()); - EXPECT_EQ(33, objval["age"]->GetInt()->GetValue()); -} - -TEST(CommandSchema, ObjectPropType_Validate) { - ObjectPropType prop; - prop.FromJson( - CreateDictionaryValue("{'properties':{'expires':'integer'," - "'password':{'maxLength':100,'minLength':6}}," - "'required':['expires','password']}") - .get(), - nullptr, nullptr); - ErrorPtr error; - EXPECT_TRUE(ValidateValue( - prop, *CreateValue("{'expires':10,'password':'abcdef'}"), &error)); - error.reset(); - - EXPECT_FALSE(ValidateValue(prop, *CreateValue("{'expires':10}"), &error)); - EXPECT_EQ("parameter_missing", error->GetCode()); - error.reset(); - - EXPECT_FALSE( - ValidateValue(prop, *CreateValue("{'password':'abcdef'}"), &error)); - EXPECT_EQ("parameter_missing", error->GetCode()); - error.reset(); - - EXPECT_FALSE(ValidateValue( - prop, *CreateValue("{'expires':10,'password':'abcde'}"), &error)); - EXPECT_EQ("out_of_range", error->GetFirstError()->GetCode()); - error.reset(); - - EXPECT_FALSE(ValidateValue(prop, *CreateValue("2"), &error)); - EXPECT_EQ("type_mismatch", error->GetCode()); - error.reset(); - - EXPECT_FALSE(ValidateValue( - prop, *CreateValue("{'expires':10,'password':'abcdef','retry':true}"), - &error)); - EXPECT_EQ("unexpected_parameter", error->GetCode()); - error.reset(); -} - -TEST(CommandSchema, ObjectPropType_Validate_Enum) { - ObjectPropType prop; - EXPECT_TRUE(prop.FromJson( - CreateDictionaryValue( - "{'properties':{'width':'integer','height':'integer'}," - "'enum':[{'width':10,'height':20},{'width':100,'height':200}]}") - .get(), - nullptr, nullptr)); - ErrorPtr error; - EXPECT_TRUE( - ValidateValue(prop, *CreateValue("{'height':20,'width':10}"), &error)); - error.reset(); - - EXPECT_TRUE( - ValidateValue(prop, *CreateValue("{'height':200,'width':100}"), &error)); - error.reset(); - - EXPECT_FALSE( - ValidateValue(prop, *CreateValue("{'height':12,'width':10}"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); -} - -TEST(CommandSchema, ObjectPropType_CreateValue) { - ObjectPropType prop; - IntPropType int_type; - ASSERT_TRUE(prop.FromJson( - CreateDictionaryValue( - "{'properties':{'width':'integer','height':'integer'}," - "'enum':[{'width':10,'height':20},{'width':100,'height':200}]}") - .get(), - nullptr, nullptr)); - ValueMap obj{ - {"width", int_type.CreateValue(base::FundamentalValue{10}, nullptr)}, - {"height", int_type.CreateValue(base::FundamentalValue{20}, nullptr)}, - }; - - ErrorPtr error; - auto val = prop.CreateValue( - *CreateDictionaryValue("{'width': 10, 'height': 20}"), &error); - ASSERT_NE(nullptr, val.get()); - EXPECT_EQ(nullptr, error.get()); - EXPECT_EQ(obj, val->GetValue()); - - val = prop.CreateValue(base::StringValue{"blah"}, &error); - EXPECT_EQ(nullptr, val.get()); - ASSERT_NE(nullptr, error.get()); - EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); -} - -/////////////////////////////////////////////////////////////////////////////// - -TEST(CommandSchema, ArrayPropType_Empty) { - ArrayPropType prop; - EXPECT_FALSE(prop.HasOverriddenAttributes()); - EXPECT_FALSE(prop.IsBasedOnSchema()); - EXPECT_EQ(nullptr, prop.GetDefaultValue()); - EXPECT_EQ(nullptr, prop.GetItemTypePtr()); - prop.SetItemType(PropType::Create(ValueType::Int)); - EXPECT_TRUE(prop.HasOverriddenAttributes()); - EXPECT_FALSE(prop.IsBasedOnSchema()); - EXPECT_NE(nullptr, prop.GetItemTypePtr()); - EXPECT_FALSE(prop.IsRequired()); -} - -TEST(CommandSchema, ArrayPropType_Types) { - ArrayPropType prop; - EXPECT_EQ(nullptr, prop.GetInt()); - EXPECT_EQ(nullptr, prop.GetBoolean()); - EXPECT_EQ(nullptr, prop.GetDouble()); - EXPECT_EQ(nullptr, prop.GetString()); - EXPECT_EQ(nullptr, prop.GetObject()); - EXPECT_EQ(&prop, prop.GetArray()); -} - -TEST(CommandSchema, ArrayPropType_ToJson) { - ArrayPropType prop; - prop.SetItemType(PropType::Create(ValueType::Int)); - EXPECT_JSON_EQ("{'items':'integer'}", *prop.ToJson(false, false)); - EXPECT_JSON_EQ("{'items':{'type':'integer'},'type':'array'}", - *prop.ToJson(true, false)); - EXPECT_FALSE(prop.IsBasedOnSchema()); - ArrayPropType prop2; - prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr); - EXPECT_JSON_EQ("{}", *prop2.ToJson(false, false)); - EXPECT_TRUE(prop2.IsBasedOnSchema()); - prop2.FromJson(CreateDictionaryValue("{'default':[1,2,3]}").get(), &prop, - nullptr); - EXPECT_JSON_EQ("{'default':[1,2,3]}", *prop2.ToJson(false, false)); - EXPECT_JSON_EQ( - "{'default':[1,2,3],'items':{'type':'integer'},'type':'array'}", - *prop2.ToJson(true, false)); -} - -TEST(CommandSchema, ArrayPropType_FromJson) { - ArrayPropType prop; - EXPECT_TRUE(prop.FromJson(CreateDictionaryValue("{'items':'integer'}").get(), - nullptr, nullptr)); - EXPECT_EQ(ValueType::Int, prop.GetItemTypePtr()->GetType()); - - ArrayPropType prop2; - ASSERT_TRUE( - prop2.FromJson(CreateDictionaryValue( - "{'items':'string','default':['foo', 'bar', 'baz']}") - .get(), - nullptr, nullptr)); - ASSERT_NE(nullptr, prop2.GetDefaultValue()); - const ArrayValue* defval = prop2.GetDefaultValue()->GetArray(); - ASSERT_NE(nullptr, defval); - EXPECT_EQ((std::vector<std::string>{"foo", "bar", "baz"}), - GetArrayValues<std::string>(defval->GetValue())); -} - -TEST(CommandSchema, ArrayPropType_Validate) { - ArrayPropType prop; - prop.FromJson( - CreateDictionaryValue("{'items':{'minimum':2.3, 'maximum':10.5}}").get(), - nullptr, nullptr); - - ErrorPtr error; - EXPECT_TRUE(ValidateValue(prop, *CreateValue("[3,4,10.5]"), &error)); - error.reset(); - - EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2]"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - - EXPECT_FALSE(ValidateValue(prop, *CreateValue("[4, 5, 20]"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); -} - -TEST(CommandSchema, ArrayPropType_Validate_Enum) { - ArrayPropType prop; - prop.FromJson( - CreateDictionaryValue("{'items':'integer', 'enum':[[1], [2,3], [4,5,6]]}") - .get(), - nullptr, nullptr); - - ErrorPtr error; - EXPECT_TRUE(ValidateValue(prop, *CreateValue("[2,3]"), &error)); - error.reset(); - - EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2]"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); - - EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2,3,4]"), &error)); - EXPECT_EQ("out_of_range", error->GetCode()); - error.reset(); -} - -TEST(CommandSchema, ArrayPropType_CreateValue) { - ArrayPropType prop; - ASSERT_TRUE(prop.FromJson( - CreateDictionaryValue( - "{'items':{'properties':{'width':'integer','height':'integer'}}}") - .get(), - nullptr, nullptr)); - - ErrorPtr error; - ValueVector arr; - - auto val = prop.CreateValue(base::ListValue{}, &error); - ASSERT_NE(nullptr, val.get()); - EXPECT_EQ(nullptr, error.get()); - EXPECT_EQ(arr, val->GetValue()); - EXPECT_JSON_EQ("[]", *val->ToJson()); - - val = prop.CreateValue( - *CreateValue("[{'height':20,'width':10},{'width':17, 'height':18}]"), - &error); - ASSERT_NE(nullptr, val.get()); - EXPECT_EQ(nullptr, error.get()); - EXPECT_JSON_EQ("[{'height':20,'width':10},{'height':18,'width':17}]", - *val->ToJson()); - - val = prop.CreateValue(base::StringValue{"blah"}, &error); - EXPECT_EQ(nullptr, val.get()); - ASSERT_NE(nullptr, error.get()); - EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); -} - -TEST(CommandSchema, ArrayPropType_NestedArrays_NotSupported) { - ArrayPropType prop; - ErrorPtr error; - EXPECT_FALSE(prop.FromJson( - CreateDictionaryValue("{'items':{'items':'integer'}}").get(), nullptr, - &error)); - EXPECT_EQ(errors::commands::kInvalidObjectSchema, error->GetCode()); - error.reset(); -} - -/////////////////////////////////////////////////////////////////////////////// - -TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeName) { - ObjectSchema schema; - const char* schema_str = - "{" - "'param1':'integer'," - "'param2':'number'," - "'param3':'string'" - "}"; - EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - nullptr)); - EXPECT_EQ(ValueType::Int, schema.GetProp("param1")->GetType()); - EXPECT_EQ(ValueType::Double, schema.GetProp("param2")->GetType()); - EXPECT_EQ(ValueType::String, schema.GetProp("param3")->GetType()); - EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString()); - EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString()); - EXPECT_EQ(nullptr, schema.GetProp("param4")); - - int min_int = (std::numeric_limits<int>::min)(); - int max_int = (std::numeric_limits<int>::max)(); - double min_dbl = (std::numeric_limits<double>::lowest)(); - double max_dbl = (std::numeric_limits<double>::max)(); - EXPECT_EQ(min_int, schema.GetProp("param1")->GetInt()->GetMinValue()); - EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue()); - EXPECT_EQ(min_dbl, schema.GetProp("param2")->GetDouble()->GetMinValue()); - EXPECT_EQ(max_dbl, schema.GetProp("param2")->GetDouble()->GetMaxValue()); - EXPECT_EQ(0, schema.GetProp("param3")->GetString()->GetMinLength()); - EXPECT_EQ(max_int, schema.GetProp("param3")->GetString()->GetMaxLength()); -} - -TEST(CommandSchema, ObjectSchema_FromJson_Full_TypeName) { - ObjectSchema schema; - const char* schema_str = - "{" - "'param1':{'type':'integer'}," - "'param2':{'type':'number'}," - "'param3':{'type':'string'}," - "'param4':{'type':'array', 'items':'integer'}," - "'param5':{'type':'object', 'properties':{'p1':'integer'}}" - "}"; - EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - nullptr)); - EXPECT_EQ(ValueType::Int, schema.GetProp("param1")->GetType()); - EXPECT_EQ(ValueType::Double, schema.GetProp("param2")->GetType()); - EXPECT_EQ(ValueType::String, schema.GetProp("param3")->GetType()); - EXPECT_EQ(ValueType::Array, schema.GetProp("param4")->GetType()); - EXPECT_EQ(ValueType::Object, schema.GetProp("param5")->GetType()); - EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString()); - EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString()); - EXPECT_EQ("array", schema.GetProp("param4")->GetTypeAsString()); - EXPECT_EQ("object", schema.GetProp("param5")->GetTypeAsString()); - EXPECT_EQ(nullptr, schema.GetProp("param77")); - - int min_int = (std::numeric_limits<int>::min)(); - int max_int = (std::numeric_limits<int>::max)(); - double min_dbl = (std::numeric_limits<double>::lowest)(); - double max_dbl = (std::numeric_limits<double>::max)(); - EXPECT_EQ(min_int, schema.GetProp("param1")->GetInt()->GetMinValue()); - EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue()); - EXPECT_EQ(min_dbl, schema.GetProp("param2")->GetDouble()->GetMinValue()); - EXPECT_EQ(max_dbl, schema.GetProp("param2")->GetDouble()->GetMaxValue()); - EXPECT_EQ(0, schema.GetProp("param3")->GetString()->GetMinLength()); - EXPECT_EQ(max_int, schema.GetProp("param3")->GetString()->GetMaxLength()); -} - -TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeDeduction_Scalar) { - ObjectSchema schema; - const char* schema_str = - "{" - "'param1' :{'minimum':2}," - "'param2' :{'maximum':10}," - "'param3' :{'maximum':8, 'minimum':2}," - "'param4' :{'minimum':2.1}," - "'param5' :{'maximum':10.1}," - "'param6' :{'maximum':8.1, 'minimum':3.1}," - "'param7' :{'maximum':8, 'minimum':3.1}," - "'param8' :{'maximum':8.1, 'minimum':3}," - "'param9' :{'minLength':2}," - "'param10':{'maxLength':10}," - "'param11':{'maxLength':8, 'minLength':3}," - "'param12':{'default':12}," - "'param13':{'default':13.5}," - "'param14':{'default':true}," - "'param15':{'default':false}," - "'param16':{'default':'foobar'}," - "'param17':{'default':[1,2,3]}," - "'param18':{'items':'number', 'default':[]}" - "}"; - EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - nullptr)); - EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); - EXPECT_EQ("integer", schema.GetProp("param2")->GetTypeAsString()); - EXPECT_EQ("integer", schema.GetProp("param3")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param4")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param6")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param7")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString()); - EXPECT_EQ("string", schema.GetProp("param9")->GetTypeAsString()); - EXPECT_EQ("string", schema.GetProp("param10")->GetTypeAsString()); - EXPECT_EQ("string", schema.GetProp("param11")->GetTypeAsString()); - EXPECT_EQ("integer", schema.GetProp("param12")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param13")->GetTypeAsString()); - EXPECT_EQ("boolean", schema.GetProp("param14")->GetTypeAsString()); - EXPECT_EQ("boolean", schema.GetProp("param15")->GetTypeAsString()); - EXPECT_EQ("string", schema.GetProp("param16")->GetTypeAsString()); - EXPECT_EQ("array", schema.GetProp("param17")->GetTypeAsString()); - auto prop17 = schema.GetProp("param17"); - EXPECT_EQ("integer", prop17->GetArray()->GetItemTypePtr()->GetTypeAsString()); - EXPECT_EQ("array", schema.GetProp("param18")->GetTypeAsString()); - auto prop18 = schema.GetProp("param18"); - EXPECT_EQ("number", prop18->GetArray()->GetItemTypePtr()->GetTypeAsString()); - - int min_int = (std::numeric_limits<int>::min)(); - int max_int = (std::numeric_limits<int>::max)(); - double min_dbl = (std::numeric_limits<double>::lowest)(); - double max_dbl = (std::numeric_limits<double>::max)(); - EXPECT_EQ(2, schema.GetProp("param1")->GetInt()->GetMinValue()); - EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue()); - EXPECT_EQ(min_int, schema.GetProp("param2")->GetInt()->GetMinValue()); - EXPECT_EQ(10, schema.GetProp("param2")->GetInt()->GetMaxValue()); - EXPECT_EQ(2, schema.GetProp("param3")->GetInt()->GetMinValue()); - EXPECT_EQ(8, schema.GetProp("param3")->GetInt()->GetMaxValue()); - EXPECT_DOUBLE_EQ(2.1, schema.GetProp("param4")->GetDouble()->GetMinValue()); - EXPECT_DOUBLE_EQ(max_dbl, - schema.GetProp("param4")->GetDouble()->GetMaxValue()); - EXPECT_DOUBLE_EQ(min_dbl, - schema.GetProp("param5")->GetDouble()->GetMinValue()); - EXPECT_DOUBLE_EQ(10.1, schema.GetProp("param5")->GetDouble()->GetMaxValue()); - EXPECT_DOUBLE_EQ(3.1, schema.GetProp("param6")->GetDouble()->GetMinValue()); - EXPECT_DOUBLE_EQ(8.1, schema.GetProp("param6")->GetDouble()->GetMaxValue()); - EXPECT_DOUBLE_EQ(3.1, schema.GetProp("param7")->GetDouble()->GetMinValue()); - EXPECT_DOUBLE_EQ(8.0, schema.GetProp("param7")->GetDouble()->GetMaxValue()); - EXPECT_DOUBLE_EQ(3.0, schema.GetProp("param8")->GetDouble()->GetMinValue()); - EXPECT_DOUBLE_EQ(8.1, schema.GetProp("param8")->GetDouble()->GetMaxValue()); - EXPECT_EQ(2, schema.GetProp("param9")->GetString()->GetMinLength()); - EXPECT_EQ(max_int, schema.GetProp("param9")->GetString()->GetMaxLength()); - EXPECT_EQ(0, schema.GetProp("param10")->GetString()->GetMinLength()); - EXPECT_EQ(10, schema.GetProp("param10")->GetString()->GetMaxLength()); - EXPECT_EQ(3, schema.GetProp("param11")->GetString()->GetMinLength()); - EXPECT_EQ(8, schema.GetProp("param11")->GetString()->GetMaxLength()); - const PropValue* val = schema.GetProp("param12")->GetDefaultValue(); - EXPECT_EQ(12, val->GetInt()->GetValue()); - val = schema.GetProp("param13")->GetDefaultValue(); - EXPECT_DOUBLE_EQ(13.5, val->GetDouble()->GetValue()); - val = schema.GetProp("param14")->GetDefaultValue(); - EXPECT_TRUE(val->GetBoolean()->GetValue()); - val = schema.GetProp("param15")->GetDefaultValue(); - EXPECT_FALSE(val->GetBoolean()->GetValue()); - val = schema.GetProp("param16")->GetDefaultValue(); - EXPECT_EQ("foobar", val->GetString()->GetValue()); - val = schema.GetProp("param17")->GetDefaultValue(); - EXPECT_EQ((std::vector<int>{1, 2, 3}), - GetArrayValues<int>(val->GetArray()->GetValue())); - val = schema.GetProp("param18")->GetDefaultValue(); - EXPECT_TRUE(val->GetArray()->GetValue().empty()); -} - -TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeDeduction_Array) { - ObjectSchema schema; - const char* schema_str = - "{" - "'param1' :[0,1,2,3]," - "'param2' :[0.0,1.1,2.2]," - "'param3' :['id1', 'id2']," - "'param4' :{'enum':[1,2,3]}," - "'param5' :{'enum':[-1.1,2.2,3]}," - "'param6' :{'enum':['id0', 'id1']}," - "'param7' :{'type':'integer', 'enum':[1,2,3]}," - "'param8' :{'type':'number', 'enum':[1,2,3]}," - "'param9' :{'type':'number', 'enum':[]}," - "'param10':{'type':'integer', 'enum':[]}," - "'param11':[[0,1],[2,3]]," - "'param12':[['foo','bar']]," - "'param13':{'enum':[['id0', 'id1']]}" - "}"; - EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - nullptr)); - EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString()); - EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString()); - EXPECT_EQ("integer", schema.GetProp("param4")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString()); - EXPECT_EQ("string", schema.GetProp("param6")->GetTypeAsString()); - EXPECT_EQ("integer", schema.GetProp("param7")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString()); - EXPECT_EQ("number", schema.GetProp("param9")->GetTypeAsString()); - EXPECT_EQ("integer", schema.GetProp("param10")->GetTypeAsString()); - - auto prop_type11 = schema.GetProp("param11"); - EXPECT_EQ("array", prop_type11->GetTypeAsString()); - EXPECT_EQ("integer", - prop_type11->GetArray()->GetItemTypePtr()->GetTypeAsString()); - - auto prop_type12 = schema.GetProp("param12"); - EXPECT_EQ("array", prop_type12->GetTypeAsString()); - EXPECT_EQ("string", - prop_type12->GetArray()->GetItemTypePtr()->GetTypeAsString()); - - auto prop_type13 = schema.GetProp("param13"); - EXPECT_EQ("array", prop_type12->GetTypeAsString()); - EXPECT_EQ("string", - prop_type13->GetArray()->GetItemTypePtr()->GetTypeAsString()); - - EXPECT_EQ((std::vector<int>{0, 1, 2, 3}), - GetOneOfValues<int>(schema.GetProp("param1"))); - EXPECT_EQ((std::vector<double>{0.0, 1.1, 2.2}), - GetOneOfValues<double>(schema.GetProp("param2"))); - EXPECT_EQ((std::vector<std::string>{"id1", "id2"}), - GetOneOfValues<std::string>(schema.GetProp("param3"))); - - EXPECT_EQ((std::vector<int>{1, 2, 3}), - GetOneOfValues<int>(schema.GetProp("param4"))); - EXPECT_EQ((std::vector<double>{-1.1, 2.2, 3.0}), - GetOneOfValues<double>(schema.GetProp("param5"))); - EXPECT_EQ((std::vector<std::string>{"id0", "id1"}), - GetOneOfValues<std::string>(schema.GetProp("param6"))); - EXPECT_EQ((std::vector<int>{1, 2, 3}), - GetOneOfValues<int>(schema.GetProp("param7"))); - EXPECT_EQ((std::vector<double>{1.0, 2.0, 3.0}), - GetOneOfValues<double>(schema.GetProp("param8"))); - EXPECT_TRUE(GetOneOfValues<double>(schema.GetProp("param9")).empty()); - EXPECT_TRUE(GetOneOfValues<int>(schema.GetProp("param10")).empty()); -} - -TEST(CommandSchema, ObjectSchema_FromJson_Inheritance) { - const char* base_schema_str = - "{" - "'param0' :{'minimum':1, 'maximum':5}," - "'param1' :{'minimum':1, 'maximum':5}," - "'param2' :{'minimum':1, 'maximum':5}," - "'param3' :{'minimum':1, 'maximum':5}," - "'param4' :{'minimum':1, 'maximum':5}," - "'param5' :{'minimum':1.1, 'maximum':5.5}," - "'param6' :{'minimum':1.1, 'maximum':5.5}," - "'param7' :{'minimum':1.1, 'maximum':5.5}," - "'param8' :{'minimum':1.1, 'maximum':5.5}," - "'param9' :{'minLength':1, 'maxLength':5}," - "'param10':{'minLength':1, 'maxLength':5}," - "'param11':{'minLength':1, 'maxLength':5}," - "'param12':{'minLength':1, 'maxLength':5}," - "'param13':[1,2,3]," - "'param14':[1,2,3]," - "'param15':[1.1,2.2,3.3]," - "'param16':[1.1,2.2,3.3]," - "'param17':['id1', 'id2']," - "'param18':['id1', 'id2']," - "'param19':{'minimum':1, 'maximum':5}," - "'param20':{'default':49}," - "'param21':{'default':49}," - "'param22':'integer'" - "}"; - ObjectSchema base_schema; - EXPECT_TRUE(base_schema.FromJson(CreateDictionaryValue(base_schema_str).get(), - nullptr, nullptr)); - const char* schema_str = - "{" - "'param1' :{}," - "'param2' :{'minimum':2}," - "'param3' :{'maximum':9}," - "'param4' :{'minimum':2, 'maximum':9}," - "'param5' :{}," - "'param6' :{'minimum':2.2}," - "'param7' :{'maximum':9.9}," - "'param8' :{'minimum':2.2, 'maximum':9.9}," - "'param9' :{}," - "'param10':{'minLength':3}," - "'param11':{'maxLength':8}," - "'param12':{'minLength':3, 'maxLength':8}," - "'param13':{}," - "'param14':[1,2,3,4]," - "'param15':{}," - "'param16':[1.1,2.2,3.3,4.4]," - "'param17':{}," - "'param18':['id1', 'id3']," - "'param19':{}," - "'param20':{}," - "'param21':{'default':8}," - "'param22':{'default':123}" - "}"; - ObjectSchema schema; - EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), - &base_schema, nullptr)); - EXPECT_EQ(nullptr, schema.GetProp("param0")); - EXPECT_NE(nullptr, schema.GetProp("param1")); - EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString()); - EXPECT_EQ(1, schema.GetProp("param1")->GetInt()->GetMinValue()); - EXPECT_EQ(5, schema.GetProp("param1")->GetInt()->GetMaxValue()); - EXPECT_EQ("integer", schema.GetProp("param2")->GetTypeAsString()); - EXPECT_EQ(2, schema.GetProp("param2")->GetInt()->GetMinValue()); - EXPECT_EQ(5, schema.GetProp("param2")->GetInt()->GetMaxValue()); - EXPECT_EQ("integer", schema.GetProp("param3")->GetTypeAsString()); - EXPECT_EQ(1, schema.GetProp("param3")->GetInt()->GetMinValue()); - EXPECT_EQ(9, schema.GetProp("param3")->GetInt()->GetMaxValue()); - EXPECT_EQ("integer", schema.GetProp("param4")->GetTypeAsString()); - EXPECT_EQ(2, schema.GetProp("param4")->GetInt()->GetMinValue()); - EXPECT_EQ(9, schema.GetProp("param4")->GetInt()->GetMaxValue()); - EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString()); - EXPECT_EQ(1.1, schema.GetProp("param5")->GetDouble()->GetMinValue()); - EXPECT_EQ(5.5, schema.GetProp("param5")->GetDouble()->GetMaxValue()); - EXPECT_EQ("number", schema.GetProp("param6")->GetTypeAsString()); - EXPECT_EQ(2.2, schema.GetProp("param6")->GetDouble()->GetMinValue()); - EXPECT_EQ(5.5, schema.GetProp("param6")->GetDouble()->GetMaxValue()); - EXPECT_EQ("number", schema.GetProp("param7")->GetTypeAsString()); - EXPECT_EQ(1.1, schema.GetProp("param7")->GetDouble()->GetMinValue()); - EXPECT_EQ(9.9, schema.GetProp("param7")->GetDouble()->GetMaxValue()); - EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString()); - EXPECT_EQ(2.2, schema.GetProp("param8")->GetDouble()->GetMinValue()); - EXPECT_EQ(9.9, schema.GetProp("param8")->GetDouble()->GetMaxValue()); - EXPECT_EQ("string", schema.GetProp("param9")->GetTypeAsString()); - EXPECT_EQ(1, schema.GetProp("param9")->GetString()->GetMinLength()); - EXPECT_EQ(5, schema.GetProp("param9")->GetString()->GetMaxLength()); - EXPECT_EQ("string", schema.GetProp("param10")->GetTypeAsString()); - EXPECT_EQ(3, schema.GetProp("param10")->GetString()->GetMinLength()); - EXPECT_EQ(5, schema.GetProp("param10")->GetString()->GetMaxLength()); - EXPECT_EQ("string", schema.GetProp("param11")->GetTypeAsString()); - EXPECT_EQ(1, schema.GetProp("param11")->GetString()->GetMinLength()); - EXPECT_EQ(8, schema.GetProp("param11")->GetString()->GetMaxLength()); - EXPECT_EQ("string", schema.GetProp("param12")->GetTypeAsString()); - EXPECT_EQ(3, schema.GetProp("param12")->GetString()->GetMinLength()); - EXPECT_EQ(8, schema.GetProp("param12")->GetString()->GetMaxLength()); - EXPECT_EQ("integer", schema.GetProp("param13")->GetTypeAsString()); - EXPECT_EQ((std::vector<int>{1, 2, 3}), - GetOneOfValues<int>(schema.GetProp("param13"))); - EXPECT_EQ("integer", schema.GetProp("param14")->GetTypeAsString()); - EXPECT_EQ((std::vector<int>{1, 2, 3, 4}), - GetOneOfValues<int>(schema.GetProp("param14"))); - EXPECT_EQ("number", schema.GetProp("param15")->GetTypeAsString()); - EXPECT_EQ((std::vector<double>{1.1, 2.2, 3.3}), - GetOneOfValues<double>(schema.GetProp("param15"))); - EXPECT_EQ("number", schema.GetProp("param16")->GetTypeAsString()); - EXPECT_EQ((std::vector<double>{1.1, 2.2, 3.3, 4.4}), - GetOneOfValues<double>(schema.GetProp("param16"))); - EXPECT_EQ("string", schema.GetProp("param17")->GetTypeAsString()); - EXPECT_EQ((std::vector<std::string>{"id1", "id2"}), - GetOneOfValues<std::string>(schema.GetProp("param17"))); - EXPECT_EQ("string", schema.GetProp("param18")->GetTypeAsString()); - EXPECT_EQ((std::vector<std::string>{"id1", "id3"}), - GetOneOfValues<std::string>(schema.GetProp("param18"))); - EXPECT_EQ("integer", schema.GetProp("param19")->GetTypeAsString()); - EXPECT_EQ(1, schema.GetProp("param19")->GetInt()->GetMinValue()); - EXPECT_EQ(5, schema.GetProp("param19")->GetInt()->GetMaxValue()); - EXPECT_EQ(49, - schema.GetProp("param20")->GetDefaultValue()->GetInt()->GetValue()); - EXPECT_EQ(8, - schema.GetProp("param21")->GetDefaultValue()->GetInt()->GetValue()); - EXPECT_EQ(123, - schema.GetProp("param22")->GetDefaultValue()->GetInt()->GetValue()); -} - -TEST(CommandSchema, ObjectSchema_UseDefaults) { - ObjectPropType prop; - const char* schema_str = - "{'properties':{" - "'param1':{'default':true}," - "'param2':{'default':2}," - "'param3':{'default':3.3}," - "'param4':{'default':'four'}," - "'param5':{'default':{'x':5,'y':6}," - "'properties':{'x':'integer','y':'integer'}}," - "'param6':{'default':[1,2,3]}" - "}}"; - ASSERT_TRUE( - prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr)); - - // Omit all. - auto value = - prop.CreatePropValue(*CreateDictionaryValue("{}").get(), nullptr); - ASSERT_NE(nullptr, value); - ValueMap obj = value->GetObject()->GetValue(); - EXPECT_TRUE(obj["param1"]->GetBoolean()->GetValue()); - EXPECT_EQ(2, obj["param2"]->GetInt()->GetValue()); - EXPECT_DOUBLE_EQ(3.3, obj["param3"]->GetDouble()->GetValue()); - EXPECT_EQ("four", obj["param4"]->GetString()->GetValue()); - ValueMap param5 = obj["param5"]->GetObject()->GetValue(); - EXPECT_EQ(5, param5["x"]->GetInt()->GetValue()); - EXPECT_EQ(6, param5["y"]->GetInt()->GetValue()); - ValueVector param6 = obj["param6"]->GetArray()->GetValue(); - EXPECT_EQ((std::vector<int>{1, 2, 3}), GetArrayValues<int>(param6)); - - // Specify some. - const char* val_json = - "{" - "'param1':false," - "'param3':33.3," - "'param5':{'x':-5,'y':-6}" - "}"; - value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr); - ASSERT_NE(nullptr, value); - obj = value->GetObject()->GetValue(); - EXPECT_FALSE(obj["param1"]->GetBoolean()->GetValue()); - EXPECT_EQ(2, obj["param2"]->GetInt()->GetValue()); - EXPECT_DOUBLE_EQ(33.3, obj["param3"]->GetDouble()->GetValue()); - EXPECT_EQ("four", obj["param4"]->GetString()->GetValue()); - param5 = obj["param5"]->GetObject()->GetValue(); - EXPECT_EQ(-5, param5["x"]->GetInt()->GetValue()); - EXPECT_EQ(-6, param5["y"]->GetInt()->GetValue()); - param6 = obj["param6"]->GetArray()->GetValue(); - EXPECT_EQ((std::vector<int>{1, 2, 3}), GetArrayValues<int>(param6)); - - // Specify all. - val_json = - "{" - "'param1':false," - "'param2':22," - "'param3':333.3," - "'param4':'FOUR'," - "'param5':{'x':-55,'y':66}," - "'param6':[-1, 0]" - "}"; - value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr); - ASSERT_NE(nullptr, value); - obj = value->GetObject()->GetValue(); - EXPECT_FALSE(obj["param1"]->GetBoolean()->GetValue()); - EXPECT_EQ(22, obj["param2"]->GetInt()->GetValue()); - EXPECT_DOUBLE_EQ(333.3, obj["param3"]->GetDouble()->GetValue()); - EXPECT_EQ("FOUR", obj["param4"]->GetString()->GetValue()); - param5 = obj["param5"]->GetObject()->GetValue(); - EXPECT_EQ(-55, param5["x"]->GetInt()->GetValue()); - EXPECT_EQ(66, param5["y"]->GetInt()->GetValue()); - param6 = obj["param6"]->GetArray()->GetValue(); - EXPECT_EQ((std::vector<int>{-1, 0}), GetArrayValues<int>(param6)); -} - -TEST(CommandSchema, ObjectSchema_FromJson_BaseSchema_Failures) { - ObjectSchema schema; - ErrorPtr error; - const char* schema_str = - "{" - "'param1':{}" - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':{'type':'foo'}" - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("unknown_type", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':[]" - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':{'minimum':'foo'}" - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':[1,2.2]" - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':{'minimum':1, 'enum':[1,2,3]}" // can't have min/max & enum. - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("unexpected_parameter", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':{'maximum':1, 'blah':2}" // 'blah' is unexpected. - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("unexpected_parameter", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':{'enum':[1,2,3],'default':5}" // 'default' must be 1, 2, or 3. - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("out_of_range", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':[[1,2.3]]" - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':[[1,2],[3,4],['blah']]" - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':{'default':[]}" - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':[[[1]],[[2]]]" - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':{'enum':[[['foo']]]}" - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); - error.reset(); - - schema_str = - "{" - "'param1':{'default':[[1],[2]]}" - "}"; - EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - &error)); - EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode()); - error.reset(); -} - -TEST(CommandSchema, RequiredProperties_Integral) { - IntPropType prop; - - prop.MakeRequired(false); - EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false)); - EXPECT_JSON_EQ("{'isRequired':false,'type':'integer'}", - *prop.ToJson(true, true)); - - prop.MakeRequired(true); - EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false)); - EXPECT_JSON_EQ("{'isRequired':true,'type':'integer'}", - *prop.ToJson(true, true)); - - IntPropType prop2; - EXPECT_TRUE( - prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr)); - EXPECT_TRUE(prop2.IsRequired()); - - EXPECT_TRUE(prop2.FromJson( - CreateDictionaryValue("{'isRequired': false}").get(), &prop, nullptr)); - EXPECT_FALSE(prop2.IsRequired()); - - EXPECT_JSON_EQ("{'type':'integer'}", *prop2.ToJson(true, false)); - EXPECT_JSON_EQ("{'isRequired':false,'type':'integer'}", - *prop2.ToJson(true, true)); -} - -TEST(CommandSchema, RequiredProperties_Object) { - ObjectPropType obj_type; - auto schema = ObjectSchema::Create(); - auto type = PropType::Create(ValueType::Int); - type->MakeRequired(true); - schema->AddProp("prop1", std::move(type)); - type = PropType::Create(ValueType::String); - type->MakeRequired(false); - schema->AddProp("prop2", std::move(type)); - type = PropType::Create(ValueType::Boolean); - type->MakeRequired(true); - schema->AddProp("prop3", std::move(type)); - type = PropType::Create(ValueType::Array); - type->GetArray()->SetItemType(PropType::Create(ValueType::String)); - schema->AddProp("prop4", std::move(type)); - auto expected1 = R"({ - 'prop1': 'integer', - 'prop2': 'string', - 'prop3': 'boolean', - 'prop4': {'items': 'string'} - })"; - EXPECT_JSON_EQ(expected1, *schema->ToJson(false, false)); - auto expected2 = R"({ - 'prop1': {'type':'integer','isRequired': true}, - 'prop2': {'type':'string','isRequired': false}, - 'prop3': {'type':'boolean','isRequired': true}, - 'prop4': {'items': 'string'} - })"; - EXPECT_JSON_EQ(expected2, *schema->ToJson(false, true)); - - obj_type.SetObjectSchema(std::move(schema)); - auto expected3 = R"({ - 'additionalProperties': false, - 'properties': { - 'prop1': 'integer', - 'prop2': 'string', - 'prop3': 'boolean', - 'prop4': {'items': 'string'} - }, - 'required': ['prop1','prop3'] - })"; - EXPECT_JSON_EQ(expected3, *obj_type.ToJson(false, false)); - EXPECT_JSON_EQ(expected3, *obj_type.ToJson(false, true)); -} - -TEST(CommandSchema, RequiredProperties_Schema_FromJson) { - ObjectSchema schema; - auto schema_str = R"({ - 'prop1': {'type':'integer','isRequired': true}, - 'prop2': {'type':'string','isRequired': false}, - 'prop3': 'boolean' - })"; - EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, - nullptr)); - EXPECT_TRUE(schema.GetProp("prop1")->IsRequired()); - EXPECT_FALSE(schema.GetProp("prop2")->IsRequired()); - EXPECT_FALSE(schema.GetProp("prop3")->IsRequired()); - EXPECT_JSON_EQ(schema_str, *schema.ToJson(false, true)); -} - -TEST(CommandSchema, RequiredProperties_Schema_FromJson_Inherit) { - ObjectSchema base_schema; - auto base_schema_str = R"({ - 'prop1': {'type':'integer','isRequired': true}, - 'prop2': {'type':'integer','isRequired': false}, - 'prop3': {'type':'integer','isRequired': true}, - 'prop4': {'type':'integer','isRequired': false} - })"; - EXPECT_TRUE(base_schema.FromJson(CreateDictionaryValue(base_schema_str).get(), - nullptr, nullptr)); - ObjectSchema schema; - auto schema_str = R"({ - 'prop1': {'isRequired': false}, - 'prop2': {'isRequired': true}, - 'prop3': {}, - 'prop4': 'integer' - })"; - EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), - &base_schema, nullptr)); - EXPECT_FALSE(schema.GetProp("prop1")->IsRequired()); - EXPECT_TRUE(schema.GetProp("prop2")->IsRequired()); - EXPECT_TRUE(schema.GetProp("prop3")->IsRequired()); - EXPECT_FALSE(schema.GetProp("prop4")->IsRequired()); - auto expected = R"({ - 'prop1': {'type':'integer','isRequired': false}, - 'prop2': {'type':'integer','isRequired': true}, - 'prop3': {}, - 'prop4': {} - })"; - EXPECT_JSON_EQ(expected, *schema.ToJson(false, true)); -} - -TEST(CommandSchema, RequiredProperties_ObjectPropType_FromJson) { - ObjectPropType obj_type; - auto type_str = R"({ - 'properties': { - 'prop1': 'integer', - 'prop2': 'string', - 'prop3': {'type':'boolean','isRequired':true}, - 'prop4': {'items': 'string','isRequired':false}, - 'prop5': {'type':'number','isRequired':true} - }, - 'required': ['prop1','prop3','prop4','prop5'] - })"; - EXPECT_TRUE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr, - nullptr)); - EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop1")->IsRequired()); - EXPECT_FALSE(obj_type.GetObjectSchemaPtr()->GetProp("prop2")->IsRequired()); - EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop3")->IsRequired()); - // 'required' takes precedence over 'isRequired'. - EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop4")->IsRequired()); - EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop5")->IsRequired()); -} - -TEST(CommandSchema, RequiredProperties_Failures) { - ObjectPropType obj_type; - ErrorPtr error; - - auto type_str = R"({ - 'properties': { - 'prop1': 'integer', - 'prop2': 'string' - }, - 'required': ['prop1','prop3','prop4'] - })"; - EXPECT_FALSE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr, - &error)); - EXPECT_EQ(errors::commands::kUnknownProperty, error->GetCode()); - error.reset(); - - type_str = R"({ - 'properties': { - 'prop1': 'integer', - 'prop2': 'string' - }, - 'required': 'prop1' - })"; - EXPECT_FALSE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr, - &error)); - EXPECT_EQ(errors::commands::kInvalidObjectSchema, error->GetCode()); - error.reset(); -} - -TEST(CommandSchema, ObjectSchema_UseRequired) { - ObjectPropType prop; - auto schema_str = R"({ - 'properties':{ - 'param1':'integer', - 'param2':'integer', - 'param3':{'default':3}, - 'param4':{'default':4} - }, - 'required':['param1','param3'] - })"; - ASSERT_TRUE( - prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr)); - - auto val_json = R"({ - 'param1':10, - 'param2':20, - 'param3':30, - 'param4':40 - })"; - auto value = - prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr); - ASSERT_NE(nullptr, value); - ValueMap obj = value->GetObject()->GetValue(); - EXPECT_EQ(10, obj["param1"]->GetInt()->GetValue()); - EXPECT_EQ(20, obj["param2"]->GetInt()->GetValue()); - EXPECT_EQ(30, obj["param3"]->GetInt()->GetValue()); - EXPECT_EQ(40, obj["param4"]->GetInt()->GetValue()); - - val_json = "{'param1':100}"; - value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr); - ASSERT_NE(nullptr, value); - obj = value->GetObject()->GetValue(); - EXPECT_EQ(3u, obj.size()); - - EXPECT_EQ(100, obj["param1"]->GetInt()->GetValue()); - EXPECT_EQ(obj.end(), obj.find("param2")); - EXPECT_EQ(3, obj["param3"]->GetInt()->GetValue()); - EXPECT_EQ(4, obj["param4"]->GetInt()->GetValue()); -} - -TEST(CommandSchema, ObjectSchema_UseRequired_Failure) { - ObjectPropType prop; - auto schema_str = R"({ - 'properties':{ - 'param1':'integer', - 'param2':'integer', - 'param3':{'default':3}, - 'param4':{'default':4} - }, - 'required':['param1','param3'] - })"; - ASSERT_TRUE( - prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr)); - - auto val_json = "{'param2':20}"; - ErrorPtr error; - auto value = - prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), &error); - ASSERT_EQ(nullptr, value); - EXPECT_EQ(errors::commands::kPropertyMissing, error->GetCode()); -} - -} // namespace weave diff --git a/src/commands/prop_constraints.cc b/src/commands/prop_constraints.cc deleted file mode 100644 index b7e9cf6..0000000 --- a/src/commands/prop_constraints.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/commands/prop_constraints.h" - -#include <base/json/json_writer.h> -#include <base/logging.h> - -#include "src/commands/prop_values.h" -#include "src/commands/schema_constants.h" -#include "src/string_utils.h" - -namespace weave { - -namespace { - -// Helper function to convert a property value to string, which is used for -// error reporting. -std::string PropValueToString(const PropValue& value) { - std::string result; - auto json = value.ToJson(); - CHECK(json); - base::JSONWriter::Write(*json, &result); - return result; -} - -} // anonymous namespace - -// Constraint ---------------------------------------------------------------- -Constraint::~Constraint() {} - -bool Constraint::ReportErrorLessThan(ErrorPtr* error, - const std::string& val, - const std::string& limit) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kOutOfRange, - "Value %s is out of range. It must not be less than %s", - val.c_str(), limit.c_str()); - return false; -} - -bool Constraint::ReportErrorGreaterThan(ErrorPtr* error, - const std::string& val, - const std::string& limit) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kOutOfRange, - "Value %s is out of range. It must not be greater than %s", - val.c_str(), limit.c_str()); - return false; -} - -bool Constraint::ReportErrorNotOneOf(ErrorPtr* error, - const std::string& val, - const std::vector<std::string>& values) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kOutOfRange, - "Value %s is invalid. Expected one of [%s]", val.c_str(), - Join(",", values).c_str()); - return false; -} - -void Constraint::AddToJsonDict(base::DictionaryValue* dict, - bool overridden_only) const { - if (!overridden_only || HasOverriddenAttributes()) { - auto value = ToJson(); - CHECK(value); - dict->SetWithoutPathExpansion(GetDictKey(), value.release()); - } -} - -// ConstraintStringLength ----------------------------------------------------- -ConstraintStringLength::ConstraintStringLength( - const InheritableAttribute<int>& limit) - : limit_(limit) {} -ConstraintStringLength::ConstraintStringLength(int limit) : limit_(limit) {} - -bool ConstraintStringLength::HasOverriddenAttributes() const { - return !limit_.is_inherited; -} - -std::unique_ptr<base::Value> ConstraintStringLength::ToJson() const { - return TypedValueToJson(limit_.value); -} - -// ConstraintStringLengthMin -------------------------------------------------- -ConstraintStringLengthMin::ConstraintStringLengthMin( - const InheritableAttribute<int>& limit) - : ConstraintStringLength(limit) {} -ConstraintStringLengthMin::ConstraintStringLengthMin(int limit) - : ConstraintStringLength(limit) {} - -bool ConstraintStringLengthMin::Validate(const PropValue& value, - ErrorPtr* error) const { - CHECK(value.GetString()) << "Expecting a string value for this constraint"; - const std::string& str = value.GetString()->GetValue(); - int length = static_cast<int>(str.size()); - if (length < limit_.value) { - if (limit_.value == 1) { - Error::AddTo(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kOutOfRange, "String must not be empty"); - } else { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kOutOfRange, - "String must be at least %d characters long," - " actual length of string '%s' is %d", - limit_.value, str.c_str(), length); - } - return false; - } - return true; -} - -std::unique_ptr<Constraint> ConstraintStringLengthMin::Clone() const { - return std::unique_ptr<Constraint>{new ConstraintStringLengthMin{limit_}}; -} - -std::unique_ptr<Constraint> ConstraintStringLengthMin::CloneAsInherited() - const { - return std::unique_ptr<Constraint>{ - new ConstraintStringLengthMin{limit_.value}}; -} - -// ConstraintStringLengthMax -------------------------------------------------- -ConstraintStringLengthMax::ConstraintStringLengthMax( - const InheritableAttribute<int>& limit) - : ConstraintStringLength(limit) {} -ConstraintStringLengthMax::ConstraintStringLengthMax(int limit) - : ConstraintStringLength(limit) {} - -bool ConstraintStringLengthMax::Validate(const PropValue& value, - ErrorPtr* error) const { - CHECK(value.GetString()) << "Expecting a string value for this constraint"; - const std::string& str = value.GetString()->GetValue(); - int length = static_cast<int>(str.size()); - if (length > limit_.value) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kOutOfRange, - "String must be no more than %d character(s) " - "long, actual length of string '%s' is %d", - limit_.value, str.c_str(), length); - return false; - } - return true; -} - -std::unique_ptr<Constraint> ConstraintStringLengthMax::Clone() const { - return std::unique_ptr<Constraint>{new ConstraintStringLengthMax{limit_}}; -} - -std::unique_ptr<Constraint> ConstraintStringLengthMax::CloneAsInherited() - const { - return std::unique_ptr<Constraint>{ - new ConstraintStringLengthMax{limit_.value}}; -} - -// ConstraintOneOf -------------------------------------------------- -ConstraintOneOf::ConstraintOneOf(InheritableAttribute<ValueVector> set) - : set_(std::move(set)) {} -ConstraintOneOf::ConstraintOneOf(ValueVector set) : set_(std::move(set)) {} - -bool ConstraintOneOf::Validate(const PropValue& value, ErrorPtr* error) const { - for (const auto& item : set_.value) { - if (value.IsEqual(item.get())) - return true; - } - std::vector<std::string> choice_list; - choice_list.reserve(set_.value.size()); - for (const auto& item : set_.value) { - choice_list.push_back(PropValueToString(*item)); - } - return ReportErrorNotOneOf(error, PropValueToString(value), choice_list); -} - -std::unique_ptr<Constraint> ConstraintOneOf::Clone() const { - InheritableAttribute<ValueVector> attr; - attr.is_inherited = set_.is_inherited; - attr.value.reserve(set_.value.size()); - for (const auto& prop_value : set_.value) { - attr.value.push_back(prop_value->Clone()); - } - return std::unique_ptr<Constraint>{new ConstraintOneOf{std::move(attr)}}; -} - -std::unique_ptr<Constraint> ConstraintOneOf::CloneAsInherited() const { - ValueVector cloned; - cloned.reserve(set_.value.size()); - for (const auto& prop_value : set_.value) { - cloned.push_back(prop_value->Clone()); - } - return std::unique_ptr<Constraint>{new ConstraintOneOf{std::move(cloned)}}; -} - -std::unique_ptr<base::Value> ConstraintOneOf::ToJson() const { - return TypedValueToJson(set_.value); -} - -const char* ConstraintOneOf::GetDictKey() const { - return commands::attributes::kOneOf_Enum; -} - -} // namespace weave diff --git a/src/commands/prop_constraints.h b/src/commands/prop_constraints.h deleted file mode 100644 index 53a4d93..0000000 --- a/src/commands/prop_constraints.h +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef LIBWEAVE_SRC_COMMANDS_PROP_CONSTRAINTS_H_ -#define LIBWEAVE_SRC_COMMANDS_PROP_CONSTRAINTS_H_ - -#include <string> -#include <type_traits> -#include <vector> - -#include <base/macros.h> -#include <base/values.h> -#include <weave/error.h> - -#include "src/commands/prop_values.h" -#include "src/commands/schema_constants.h" -#include "src/commands/schema_utils.h" -#include "src/string_utils.h" - -namespace weave { - -enum class ConstraintType { Min, Max, StringLengthMin, StringLengthMax, OneOf }; - -// Abstract base class for all parameter constraints. Many constraints are -// type-dependent. Thus, a numeric parameter could have "minimum" and/or -// "maximum" constraints specified. Some constraints, such as "OneOf" apply to -// any data type. -class Constraint { - public: - Constraint() = default; - virtual ~Constraint(); - - // Gets the constraint type. - virtual ConstraintType GetType() const = 0; - - // Checks if any of the constraint properties/attributes are overridden - // from their base schema definition. If the constraint is inherited, then - // it will not be written to JSON when saving partial schema. - virtual bool HasOverriddenAttributes() const = 0; - - // Validates a parameter against the constraint. Returns true if parameter - // value satisfies the constraint, otherwise fills the optional |error| with - // the details for the failure. - virtual bool Validate(const PropValue& value, ErrorPtr* error) const = 0; - - // Makes a full copy of this Constraint instance. - virtual std::unique_ptr<Constraint> Clone() const = 0; - - // Makes a copy of the constraint object, marking all the attributes - // as inherited from the original definition. - virtual std::unique_ptr<Constraint> CloneAsInherited() const = 0; - - // Saves the constraint into the specified JSON |dict| object, representing - // the object schema. If |overridden_only| is set to true, then the - // inherited constraints will not be added to the schema object. - virtual void AddToJsonDict(base::DictionaryValue* dict, - bool overridden_only) const; - - // Saves the value of constraint to JSON value. E.g., if the numeric - // constraint was defined as {"minimum":20} this will create a JSON value - // of 20. The current design implies that each constraint has one value - // only. If this assumption changes, this interface needs to be updated - // accordingly. - virtual std::unique_ptr<base::Value> ToJson() const = 0; - - // Overloaded by the concrete class implementation, it should return the - // JSON object property name to store the constraint's value as. - // E.g., if the numeric constraint was defined as {"minimum":20} this - // method should return "minimum". - virtual const char* GetDictKey() const = 0; - - protected: - // Static helper methods to format common constraint validation errors. - // They fill the |error| object with specific error message. - // Since these functions could be used by constraint objects for various - // data types, the values used in validation are expected to be - // send as strings already. - static bool ReportErrorLessThan(ErrorPtr* error, - const std::string& val, - const std::string& limit); - static bool ReportErrorGreaterThan(ErrorPtr* error, - const std::string& val, - const std::string& limit); - - static bool ReportErrorNotOneOf(ErrorPtr* error, - const std::string& val, - const std::vector<std::string>& values); - - private: - DISALLOW_COPY_AND_ASSIGN(Constraint); -}; - -// ConstraintMinMaxBase is a base class for numeric Minimum and Maximum -// constraints. -template <typename T> -class ConstraintMinMaxBase : public Constraint { - public: - explicit ConstraintMinMaxBase(const InheritableAttribute<T>& limit) - : limit_(limit) {} - explicit ConstraintMinMaxBase(const T& limit) : limit_(limit) {} - - // Implementation of Constraint::HasOverriddenAttributes(). - bool HasOverriddenAttributes() const override { return !limit_.is_inherited; } - - // Implementation of Constraint::ToJson(). - std::unique_ptr<base::Value> ToJson() const override { - return TypedValueToJson(limit_.value); - } - - // Stores the upper/lower value limit for maximum/minimum constraint. - // |limit_.is_inherited| indicates whether the constraint is inherited - // from base schema or overridden. - InheritableAttribute<T> limit_; - - private: - DISALLOW_COPY_AND_ASSIGN(ConstraintMinMaxBase); -}; - -// Implementation of Minimum value constraint for Integer/Double types. -template <typename T> -class ConstraintMin : public ConstraintMinMaxBase<T> { - public: - explicit ConstraintMin(const InheritableAttribute<T>& limit) - : ConstraintMinMaxBase<T>(limit) {} - explicit ConstraintMin(const T& limit) : ConstraintMinMaxBase<T>(limit) {} - - // Implementation of Constraint::GetType(). - ConstraintType GetType() const override { return ConstraintType::Min; } - - // Implementation of Constraint::Validate(). - bool Validate(const PropValue& value, ErrorPtr* error) const override { - const T& v = static_cast<const TypedValueBase<T>&>(value).GetValue(); - if (v < this->limit_.value) { - return this->ReportErrorLessThan(error, std::to_string(v), - std::to_string(this->limit_.value)); - } - return true; - } - - // Implementation of Constraint::Clone(). - std::unique_ptr<Constraint> Clone() const override { - return std::unique_ptr<Constraint>{new ConstraintMin{this->limit_}}; - } - - // Implementation of Constraint::CloneAsInherited(). - std::unique_ptr<Constraint> CloneAsInherited() const override { - return std::unique_ptr<Constraint>{new ConstraintMin{this->limit_.value}}; - } - - // Implementation of Constraint::GetDictKey(). - const char* GetDictKey() const override { - return commands::attributes::kNumeric_Min; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ConstraintMin); -}; - -// Implementation of Maximum value constraint for Integer/Double types. -template <typename T> -class ConstraintMax : public ConstraintMinMaxBase<T> { - public: - explicit ConstraintMax(const InheritableAttribute<T>& limit) - : ConstraintMinMaxBase<T>(limit) {} - explicit ConstraintMax(const T& limit) : ConstraintMinMaxBase<T>(limit) {} - - // Implementation of Constraint::GetType(). - ConstraintType GetType() const override { return ConstraintType::Max; } - - // Implementation of Constraint::Validate(). - bool Validate(const PropValue& value, ErrorPtr* error) const override { - const T& v = static_cast<const TypedValueBase<T>&>(value).GetValue(); - if (v > this->limit_.value) - return this->ReportErrorGreaterThan(error, std::to_string(v), - std::to_string(this->limit_.value)); - return true; - } - - // Implementation of Constraint::Clone(). - std::unique_ptr<Constraint> Clone() const override { - return std::unique_ptr<Constraint>{new ConstraintMax{this->limit_}}; - } - - // Implementation of Constraint::CloneAsInherited(). - std::unique_ptr<Constraint> CloneAsInherited() const override { - return std::unique_ptr<Constraint>{new ConstraintMax{this->limit_.value}}; - } - - // Implementation of Constraint::GetDictKey(). - const char* GetDictKey() const override { - return commands::attributes::kNumeric_Max; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ConstraintMax); -}; - -// ConstraintStringLength is a base class for Minimum/Maximum string length -// constraints, similar to ConstraintMinMaxBase of numeric types. -class ConstraintStringLength : public Constraint { - public: - explicit ConstraintStringLength(const InheritableAttribute<int>& limit); - explicit ConstraintStringLength(int limit); - - // Implementation of Constraint::HasOverriddenAttributes(). - bool HasOverriddenAttributes() const override; - // Implementation of Constraint::ToJson(). - std::unique_ptr<base::Value> ToJson() const override; - - // Stores the upper/lower value limit for string length constraint. - // |limit_.is_inherited| indicates whether the constraint is inherited - // from base schema or overridden. - InheritableAttribute<int> limit_; - - private: - DISALLOW_COPY_AND_ASSIGN(ConstraintStringLength); -}; - -// Implementation of Minimum string length constraint. -class ConstraintStringLengthMin : public ConstraintStringLength { - public: - explicit ConstraintStringLengthMin(const InheritableAttribute<int>& limit); - explicit ConstraintStringLengthMin(int limit); - - // Implementation of Constraint::GetType(). - ConstraintType GetType() const override { - return ConstraintType::StringLengthMin; - } - - // Implementation of Constraint::Validate(). - bool Validate(const PropValue& value, ErrorPtr* error) const override; - - // Implementation of Constraint::Clone(). - std::unique_ptr<Constraint> Clone() const override; - - // Implementation of Constraint::CloneAsInherited(). - std::unique_ptr<Constraint> CloneAsInherited() const override; - // Implementation of Constraint::GetDictKey(). - const char* GetDictKey() const override { - return commands::attributes::kString_MinLength; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ConstraintStringLengthMin); -}; - -// Implementation of Maximum string length constraint. -class ConstraintStringLengthMax : public ConstraintStringLength { - public: - explicit ConstraintStringLengthMax(const InheritableAttribute<int>& limit); - explicit ConstraintStringLengthMax(int limit); - - // Implementation of Constraint::GetType(). - ConstraintType GetType() const override { - return ConstraintType::StringLengthMax; - } - - // Implementation of Constraint::Validate(). - bool Validate(const PropValue& value, ErrorPtr* error) const override; - - // Implementation of Constraint::Clone(). - std::unique_ptr<Constraint> Clone() const override; - - // Implementation of Constraint::CloneAsInherited(). - std::unique_ptr<Constraint> CloneAsInherited() const override; - - // Implementation of Constraint::GetDictKey(). - const char* GetDictKey() const override { - return commands::attributes::kString_MaxLength; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ConstraintStringLengthMax); -}; - -// Implementation of OneOf constraint for different data types. -class ConstraintOneOf : public Constraint { - public: - explicit ConstraintOneOf(InheritableAttribute<ValueVector> set); - explicit ConstraintOneOf(ValueVector set); - - // Implementation of Constraint::GetType(). - ConstraintType GetType() const override { return ConstraintType::OneOf; } - - // Implementation of Constraint::HasOverriddenAttributes(). - bool HasOverriddenAttributes() const override { return !set_.is_inherited; } - - // Implementation of Constraint::Validate(). - bool Validate(const PropValue& value, ErrorPtr* error) const override; - - // Implementation of Constraint::Clone(). - std::unique_ptr<Constraint> Clone() const override; - - // Implementation of Constraint::CloneAsInherited(). - std::unique_ptr<Constraint> CloneAsInherited() const override; - - // Implementation of Constraint::ToJson(). - std::unique_ptr<base::Value> ToJson() const override; - - // Implementation of Constraint::GetDictKey(). - const char* GetDictKey() const override; - - // Stores the list of acceptable values for the parameter. - // |set_.is_inherited| indicates whether the constraint is inherited - // from base schema or overridden. - InheritableAttribute<ValueVector> set_; - - private: - DISALLOW_COPY_AND_ASSIGN(ConstraintOneOf); -}; - -} // namespace weave - -#endif // LIBWEAVE_SRC_COMMANDS_PROP_CONSTRAINTS_H_ diff --git a/src/commands/prop_types.cc b/src/commands/prop_types.cc deleted file mode 100644 index def68b1..0000000 --- a/src/commands/prop_types.cc +++ /dev/null @@ -1,655 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/commands/prop_types.h" - -#include <algorithm> -#include <limits> -#include <set> - -#include <base/json/json_writer.h> -#include <base/logging.h> -#include <base/values.h> - -#include "src/commands/object_schema.h" -#include "src/commands/prop_values.h" -#include "src/commands/schema_constants.h" - -namespace weave { - -// PropType ------------------------------------------------------------------- -PropType::PropType() {} - -PropType::~PropType() {} - -std::string PropType::GetTypeAsString() const { - return GetTypeStringFromType(GetType()); -} - -bool PropType::HasOverriddenAttributes() const { - if (default_.value && !default_.is_inherited) - return true; - - for (const auto& pair : constraints_) { - if (pair.second->HasOverriddenAttributes()) - return true; - } - return false; -} - -bool PropType::IsRequired() const { - return required_.value; -} - -void PropType::MakeRequired(bool required) { - required_.value = required; - required_.is_inherited = false; -} - -std::unique_ptr<base::Value> PropType::ToJson(bool full_schema, - bool in_command_def) const { - // Determine if we need to output "isRequired" attribute. - const bool include_required = in_command_def && !required_.is_inherited; - - // If we must include "isRequired" attribute, then treat this as "full schema" - // request because there could be cases where we have just this attribute and - // won't be able to infer the type from the constraints only. - if (include_required) - full_schema = true; - - if (!full_schema && !HasOverriddenAttributes()) { - if (based_on_schema_) - return std::unique_ptr<base::Value>(new base::DictionaryValue); - return TypedValueToJson(GetTypeAsString()); - } - - std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue); - if (full_schema) { - // If we are asked for full_schema definition, then we need to output every - // parameter property, including the "type", and any constraints. - // So, we write the "type" only if asked for full schema. - // Otherwise we will be able to infer the parameter type based on - // the constraints and their types. - // That is, the following JSONs could possibly define a parameter: - // {'type':'integer'} -> explicit "integer" with no constraints - // {'minimum':10} -> no type specified, but since we have "minimum" - // and 10 is integer, than this is an integer - // parameter with min constraint. - // {'enum':[1,2,3]} -> integer with OneOf constraint. - // And so is this: [1,2,3] -> an array of ints assume it's an "int" enum. - dict->SetString(commands::attributes::kType, GetTypeAsString()); - } - - if (!full_schema && constraints_.size() == 1) { - // If we are not asked for full schema, and we have only one constraint - // which is OneOf, we short-circuit the whole thing and return just - // the array [1,2,3] instead of an object with "enum" property like: - // {'enum':[1,2,3]} - auto p = constraints_.find(ConstraintType::OneOf); - if (p != constraints_.end()) { - return p->second->ToJson(); - } - } - - for (const auto& pair : constraints_) - pair.second->AddToJsonDict(dict.get(), !full_schema); - - if (default_.value && (full_schema || !default_.is_inherited)) { - auto def_val = default_.value->ToJson(); - CHECK(def_val); - dict->Set(commands::attributes::kDefault, def_val.release()); - } - - if (include_required) - dict->SetBoolean(commands::attributes::kIsRequired, required_.value); - return std::unique_ptr<base::Value>(dict.release()); -} - -std::unique_ptr<PropType> PropType::Clone() const { - auto cloned = PropType::Create(GetType()); - cloned->based_on_schema_ = based_on_schema_; - for (const auto& pair : constraints_) { - cloned->constraints_.insert( - std::make_pair(pair.first, pair.second->Clone())); - } - cloned->default_.is_inherited = default_.is_inherited; - if (default_.value) - cloned->default_.value = default_.value->Clone(); - cloned->required_ = required_; - return cloned; -} - -bool PropType::FromJson(const base::DictionaryValue* value, - const PropType* base_schema, - ErrorPtr* error) { - if (base_schema && base_schema->GetType() != GetType()) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kPropTypeChanged, - "Redefining a property of type %s as %s", - base_schema->GetTypeAsString().c_str(), - GetTypeAsString().c_str()); - return false; - } - based_on_schema_ = (base_schema != nullptr); - constraints_.clear(); - // Add the well-known object properties first (like "type", "displayName", - // "default") to the list of "processed" keys so we do not complain about them - // when we check for unknown/unexpected keys below. - std::set<std::string> processed_keys{ - commands::attributes::kType, commands::attributes::kDisplayName, - commands::attributes::kDefault, commands::attributes::kIsRequired, - }; - if (!ObjectSchemaFromJson(value, base_schema, &processed_keys, error)) - return false; - if (base_schema) { - for (const auto& pair : base_schema->GetConstraints()) { - constraints_.insert( - std::make_pair(pair.first, pair.second->CloneAsInherited())); - } - } - if (!ConstraintsFromJson(value, &processed_keys, error)) - return false; - - // Now make sure there are no unexpected/unknown keys in the property schema - // definition object. - base::DictionaryValue::Iterator iter(*value); - while (!iter.IsAtEnd()) { - std::string key = iter.key(); - if (processed_keys.find(key) == processed_keys.end()) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kUnknownProperty, - "Unexpected property '%s'", key.c_str()); - return false; - } - iter.Advance(); - } - - // Read the "isRequired" attribute, if specified. - bool required = false; - if (value->GetBoolean(commands::attributes::kIsRequired, &required)) { - required_.value = required; - required_.is_inherited = false; - } else if (base_schema) { - // If we have the base schema, inherit the type's required value from it. - if (base_schema->required_.value) - required_.value = base_schema->required_.value; - required_.is_inherited = true; - } - - // Read the default value, if specified. - // We need to do this last since the current type definition must be complete, - // so we can parse and validate the value of the default. - const base::Value* defval = nullptr; // Owned by value - if (value->GetWithoutPathExpansion(commands::attributes::kDefault, &defval)) { - std::unique_ptr<PropValue> prop_value = CreatePropValue(*defval, error); - if (!prop_value) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kInvalidPropValue, - "Invalid value for property '%s'", - commands::attributes::kDefault); - return false; - } - default_.value = std::move(prop_value); - default_.is_inherited = false; - } else if (base_schema) { - // If we have the base schema, inherit the type's default value from it. - // It doesn't matter if the base schema actually has a default value - // specified or not. If it doesn't, then the current type definition will - // have no default value set either (|default_.value| is a unique_ptr to - // PropValue, which can be set to nullptr). - if (base_schema->default_.value) - default_.value = base_schema->default_.value->Clone(); - default_.is_inherited = true; - } - return true; -} - -void PropType::AddConstraint(std::unique_ptr<Constraint> constraint) { - constraints_[constraint->GetType()] = std::move(constraint); -} - -void PropType::RemoveConstraint(ConstraintType constraint_type) { - constraints_.erase(constraint_type); -} - -void PropType::RemoveAllConstraints() { - constraints_.clear(); -} - -const Constraint* PropType::GetConstraint( - ConstraintType constraint_type) const { - auto p = constraints_.find(constraint_type); - return p != constraints_.end() ? p->second.get() : nullptr; -} - -Constraint* PropType::GetConstraint(ConstraintType constraint_type) { - auto p = constraints_.find(constraint_type); - return p != constraints_.end() ? p->second.get() : nullptr; -} - -bool PropType::ValidateConstraints(const PropValue& value, - ErrorPtr* error) const { - for (const auto& pair : constraints_) { - if (!pair.second->Validate(value, error)) - return false; - } - return true; -} - -const PropType::TypeMap& PropType::GetTypeMap() { - static TypeMap map = { - {ValueType::Int, "integer"}, {ValueType::Double, "number"}, - {ValueType::String, "string"}, {ValueType::Boolean, "boolean"}, - {ValueType::Object, "object"}, {ValueType::Array, "array"}, - }; - return map; -} - -std::string PropType::GetTypeStringFromType(ValueType type) { - for (const auto& pair : GetTypeMap()) { - if (pair.first == type) - return pair.second; - } - LOG(FATAL) << "Type map is missing a type"; - return std::string(); -} - -bool PropType::GetTypeFromTypeString(const std::string& name, ValueType* type) { - for (const auto& pair : GetTypeMap()) { - if (pair.second == name) { - *type = pair.first; - return true; - } - } - return false; -} - -std::unique_ptr<PropType> PropType::Create(ValueType type) { - PropType* prop = nullptr; - switch (type) { - case ValueType::Int: - prop = new IntPropType; - break; - case ValueType::Double: - prop = new DoublePropType; - break; - case ValueType::String: - prop = new StringPropType; - break; - case ValueType::Boolean: - prop = new BooleanPropType; - break; - case ValueType::Object: - prop = new ObjectPropType; - break; - case ValueType::Array: - prop = new ArrayPropType; - break; - } - return std::unique_ptr<PropType>(prop); -} - -template <typename T> -static std::unique_ptr<Constraint> LoadOneOfConstraint( - const base::DictionaryValue* value, - const PropType* prop_type, - ErrorPtr* error) { - std::unique_ptr<Constraint> constraint; - const base::Value* list = nullptr; // Owned by |value| - CHECK(value->Get(commands::attributes::kOneOf_Enum, &list)) - << "'enum' property missing in JSON dictionary"; - ValueVector choice_list; - ArrayPropType array_type; - array_type.SetItemType(prop_type->Clone()); - if (!TypedValueFromJson(list, &array_type, &choice_list, error)) - return constraint; - InheritableAttribute<ValueVector> val(std::move(choice_list), false); - constraint.reset(new ConstraintOneOf{std::move(val)}); - return constraint; -} - -template <class ConstraintClass, typename T> -static std::unique_ptr<Constraint> LoadMinMaxConstraint( - const char* dict_key, - const base::DictionaryValue* value, - ErrorPtr* error) { - std::unique_ptr<Constraint> constraint; - InheritableAttribute<T> limit; - - const base::Value* src_val = nullptr; - CHECK(value->Get(dict_key, &src_val)) << "Unable to get min/max constraints"; - if (!TypedValueFromJson(src_val, nullptr, &limit.value, error)) - return constraint; - limit.is_inherited = false; - - constraint.reset(new ConstraintClass{limit}); - return constraint; -} - -// PropTypeBase ---------------------------------------------------------------- - -template <class Derived, class Value, typename T> -bool PropTypeBase<Derived, Value, T>::ConstraintsFromJson( - const base::DictionaryValue* value, - std::set<std::string>* processed_keys, - ErrorPtr* error) { - if (!PropType::ConstraintsFromJson(value, processed_keys, error)) - return false; - - if (value->HasKey(commands::attributes::kOneOf_Enum)) { - auto type = Clone(); - type->RemoveAllConstraints(); - auto constraint = LoadOneOfConstraint<T>(value, type.get(), error); - if (!constraint) - return false; - this->AddConstraint(std::move(constraint)); - this->RemoveConstraint(ConstraintType::Min); - this->RemoveConstraint(ConstraintType::Max); - processed_keys->insert(commands::attributes::kOneOf_Enum); - } - - return true; -} - -// NumericPropTypeBase --------------------------------------------------------- - -template <class Derived, class Value, typename T> -bool NumericPropTypeBase<Derived, Value, T>::ConstraintsFromJson( - const base::DictionaryValue* value, - std::set<std::string>* processed_keys, - ErrorPtr* error) { - if (!Base::ConstraintsFromJson(value, processed_keys, error)) - return false; - - if (processed_keys->find(commands::attributes::kOneOf_Enum) == - processed_keys->end()) { - // Process min/max constraints only if "enum" constraint wasn't already - // specified. - if (value->HasKey(commands::attributes::kNumeric_Min)) { - auto constraint = LoadMinMaxConstraint<ConstraintMin<T>, T>( - commands::attributes::kNumeric_Min, value, error); - if (!constraint) - return false; - this->AddConstraint(std::move(constraint)); - this->RemoveConstraint(ConstraintType::OneOf); - processed_keys->insert(commands::attributes::kNumeric_Min); - } - if (value->HasKey(commands::attributes::kNumeric_Max)) { - auto constraint = LoadMinMaxConstraint<ConstraintMax<T>, T>( - commands::attributes::kNumeric_Max, value, error); - if (!constraint) - return false; - this->AddConstraint(std::move(constraint)); - this->RemoveConstraint(ConstraintType::OneOf); - processed_keys->insert(commands::attributes::kNumeric_Max); - } - } - - return true; -} - -// StringPropType ------------------------------------------------------------- - -bool StringPropType::ConstraintsFromJson(const base::DictionaryValue* value, - std::set<std::string>* processed_keys, - ErrorPtr* error) { - if (!Base::ConstraintsFromJson(value, processed_keys, error)) - return false; - - if (processed_keys->find(commands::attributes::kOneOf_Enum) == - processed_keys->end()) { - // Process min/max constraints only if "enum" constraint wasn't already - // specified. - if (value->HasKey(commands::attributes::kString_MinLength)) { - auto constraint = LoadMinMaxConstraint<ConstraintStringLengthMin, int>( - commands::attributes::kString_MinLength, value, error); - if (!constraint) - return false; - AddConstraint(std::move(constraint)); - RemoveConstraint(ConstraintType::OneOf); - processed_keys->insert(commands::attributes::kString_MinLength); - } - if (value->HasKey(commands::attributes::kString_MaxLength)) { - auto constraint = LoadMinMaxConstraint<ConstraintStringLengthMax, int>( - commands::attributes::kString_MaxLength, value, error); - if (!constraint) - return false; - AddConstraint(std::move(constraint)); - RemoveConstraint(ConstraintType::OneOf); - processed_keys->insert(commands::attributes::kString_MaxLength); - } - } - return true; -} - -void StringPropType::AddLengthConstraint(int min_len, int max_len) { - InheritableAttribute<int> min_attr(min_len, false); - InheritableAttribute<int> max_attr(max_len, false); - AddConstraint(std::unique_ptr<ConstraintStringLengthMin>{ - new ConstraintStringLengthMin{min_attr}}); - AddConstraint(std::unique_ptr<ConstraintStringLengthMax>{ - new ConstraintStringLengthMax{max_attr}}); -} - -int StringPropType::GetMinLength() const { - auto slc = static_cast<const ConstraintStringLength*>( - GetConstraint(ConstraintType::StringLengthMin)); - return slc ? slc->limit_.value : 0; -} - -int StringPropType::GetMaxLength() const { - auto slc = static_cast<const ConstraintStringLength*>( - GetConstraint(ConstraintType::StringLengthMax)); - return slc ? slc->limit_.value : std::numeric_limits<int>::max(); -} - -// ObjectPropType ------------------------------------------------------------- - -ObjectPropType::ObjectPropType() - : object_schema_{ObjectSchema::Create(), false} {} - -bool ObjectPropType::HasOverriddenAttributes() const { - return PropType::HasOverriddenAttributes() || !object_schema_.is_inherited; -} - -std::unique_ptr<PropType> ObjectPropType::Clone() const { - auto cloned = Base::Clone(); - - cloned->GetObject()->object_schema_.is_inherited = - object_schema_.is_inherited; - cloned->GetObject()->object_schema_.value = object_schema_.value->Clone(); - return cloned; -} - -std::unique_ptr<base::Value> ObjectPropType::ToJson(bool full_schema, - bool in_command_def) const { - std::unique_ptr<base::Value> value = - PropType::ToJson(full_schema, in_command_def); - CHECK(value); - base::DictionaryValue* dict = nullptr; - CHECK(value->GetAsDictionary(&dict)) << "Expecting a JSON object"; - if (!object_schema_.is_inherited || full_schema) { - auto object_schema = object_schema_.value->ToJson(full_schema, false); - CHECK(object_schema); - - dict->SetWithoutPathExpansion(commands::attributes::kObject_Properties, - object_schema.release()); - dict->SetBooleanWithoutPathExpansion( - commands::attributes::kObject_AdditionalProperties, - object_schema_.value->GetExtraPropertiesAllowed()); - std::unique_ptr<base::ListValue> required{new base::ListValue}; - for (const auto& pair : object_schema_.value->GetProps()) { - if (pair.second->IsRequired()) - required->AppendString(pair.first); - } - if (required->GetSize() > 0) { - dict->Set(commands::attributes::kObject_Required, required.release()); - } - } - return value; -} - -bool ObjectPropType::ObjectSchemaFromJson(const base::DictionaryValue* value, - const PropType* base_schema, - std::set<std::string>* processed_keys, - ErrorPtr* error) { - if (!Base::ObjectSchemaFromJson(value, base_schema, processed_keys, error)) - return false; - - using commands::attributes::kObject_Properties; - using commands::attributes::kObject_AdditionalProperties; - - const ObjectSchema* base_object_schema = nullptr; - if (base_schema) - base_object_schema = base_schema->GetObject()->GetObjectSchemaPtr(); - - const base::DictionaryValue* props = nullptr; - std::unique_ptr<ObjectSchema> object_schema; - bool inherited = false; - if (value->GetDictionaryWithoutPathExpansion(kObject_Properties, &props)) { - processed_keys->insert(kObject_Properties); - object_schema.reset(new ObjectSchema); - if (!object_schema->FromJson(props, base_object_schema, error)) { - Error::AddTo(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kInvalidObjectSchema, - "Error parsing object property schema"); - return false; - } - } else if (base_object_schema) { - object_schema = base_object_schema->Clone(); - inherited = true; - } else { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kInvalidObjectSchema, - "Object type definition must include the " - "object schema ('%s' field not found)", - kObject_Properties); - return false; - } - bool extra_properties_allowed = false; - if (value->GetBooleanWithoutPathExpansion(kObject_AdditionalProperties, - &extra_properties_allowed)) { - processed_keys->insert(kObject_AdditionalProperties); - object_schema->SetExtraPropertiesAllowed(extra_properties_allowed); - inherited = false; - } - const base::Value* required = nullptr; - if (value->Get(commands::attributes::kObject_Required, &required)) { - processed_keys->insert(commands::attributes::kObject_Required); - const base::ListValue* required_list = nullptr; - if (!required->GetAsList(&required_list)) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kInvalidObjectSchema, - "Property '%s' must be an array", - commands::attributes::kObject_Required); - return false; - } - for (const base::Value* value : *required_list) { - std::string name; - if (!value->GetAsString(&name)) { - std::string json_value; - CHECK(base::JSONWriter::Write(*value, &json_value)); - Error::AddToPrintf( - error, FROM_HERE, errors::commands::kDomain, - errors::commands::kInvalidObjectSchema, - "Property '%s' contains invalid element (%s). String expected", - commands::attributes::kObject_Required, json_value.c_str()); - return false; - } - if (!object_schema->MarkPropRequired(name, error)) - return false; - inherited = false; - } - } - object_schema_.value = std::move(object_schema); - object_schema_.is_inherited = inherited; - - return true; -} - -void ObjectPropType::SetObjectSchema( - std::unique_ptr<const ObjectSchema> schema) { - object_schema_.value = std::move(schema); - object_schema_.is_inherited = false; -} - -// ArrayPropType ------------------------------------------------------------- - -ArrayPropType::ArrayPropType() {} - -bool ArrayPropType::HasOverriddenAttributes() const { - return PropType::HasOverriddenAttributes() || !item_type_.is_inherited; -} - -std::unique_ptr<PropType> ArrayPropType::Clone() const { - auto cloned = Base::Clone(); - - cloned->GetArray()->item_type_.is_inherited = item_type_.is_inherited; - cloned->GetArray()->item_type_.value = item_type_.value->Clone(); - return cloned; -} - -std::unique_ptr<base::Value> ArrayPropType::ToJson(bool full_schema, - bool in_command_def) const { - std::unique_ptr<base::Value> value = - PropType::ToJson(full_schema, in_command_def); - CHECK(value); - if (!item_type_.is_inherited || full_schema) { - base::DictionaryValue* dict = nullptr; - CHECK(value->GetAsDictionary(&dict)) << "Expecting a JSON object"; - auto type = item_type_.value->ToJson(full_schema, false); - CHECK(type); - dict->SetWithoutPathExpansion(commands::attributes::kItems, type.release()); - } - return value; -} - -bool ArrayPropType::ObjectSchemaFromJson(const base::DictionaryValue* value, - const PropType* base_schema, - std::set<std::string>* processed_keys, - ErrorPtr* error) { - if (!Base::ObjectSchemaFromJson(value, base_schema, processed_keys, error)) - return false; - - using commands::attributes::kItems; - - const PropType* base_type = nullptr; - if (base_schema) - base_type = base_schema->GetArray()->GetItemTypePtr(); - - const base::Value* type_value = nullptr; - if (value->GetWithoutPathExpansion(kItems, &type_value)) { - processed_keys->insert(kItems); - auto item_type = ObjectSchema::PropFromJson(*type_value, base_type, error); - if (!item_type) - return false; - if (item_type->GetType() == ValueType::Array) { - Error::AddTo(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kInvalidObjectSchema, - "Arrays of arrays are not supported"); - return false; - } - SetItemType(std::move(item_type)); - } else if (!item_type_.value) { - if (base_type) { - item_type_.value = base_type->Clone(); - item_type_.is_inherited = true; - } else { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kInvalidObjectSchema, - "Array type definition must include the " - "array item type ('%s' field not found)", - kItems); - return false; - } - } - return true; -} - -void ArrayPropType::SetItemType(std::unique_ptr<const PropType> item_type) { - item_type_.value = std::move(item_type); - item_type_.is_inherited = false; -} - -} // namespace weave diff --git a/src/commands/prop_types.h b/src/commands/prop_types.h deleted file mode 100644 index f784eb6..0000000 --- a/src/commands/prop_types.h +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef LIBWEAVE_SRC_COMMANDS_PROP_TYPES_H_ -#define LIBWEAVE_SRC_COMMANDS_PROP_TYPES_H_ - -#include <limits> -#include <map> -#include <memory> -#include <set> -#include <string> -#include <utility> -#include <vector> - -#include <weave/error.h> - -#include "src/commands/prop_constraints.h" -#include "src/commands/prop_values.h" - -namespace weave { - -class IntPropType; -class DoublePropType; -class StringPropType; -class BooleanPropType; -class ObjectPropType; -class ArrayPropType; - -// PropType is a base class for all parameter type definition objects. -// Property definitions of a particular type will derive from this class and -// provide type-specific implementations. -class PropType { - public: - // ConstraintMap is a type alias for a map containing parameter - // constraints. It is implemented as a map for fast look-ups of constraints - // of particular type. Also it is expected to have at most one constraint - // of each type (e.g. it makes no sense to impose two "minimum" constraints - // onto a numeric parameter). - using ConstraintMap = std::map<ConstraintType, std::unique_ptr<Constraint>>; - - PropType(); - virtual ~PropType(); - - // Gets the parameter type as an enumeration. - virtual ValueType GetType() const = 0; - // Gets the parameter type as a string. - std::string GetTypeAsString() const; - // Returns true if this parameter definition inherits a type - // definition from a base object schema. - bool IsBasedOnSchema() const { return based_on_schema_; } - // Returns a default value specified for the type, or nullptr if no default - // is available. - const PropValue* GetDefaultValue() const { return default_.value.get(); } - // Gets the constraints specified for the parameter, if any. - const ConstraintMap& GetConstraints() const { return constraints_; } - // Returns true if this value is required. Properties are marked as required - // by using "isRequired" attribute or listed in "required" array. - bool IsRequired() const; - // Sets the required attribute to the value of |required| and marks it as - // overridden (not-inherited). - void MakeRequired(bool required); - // Checks if any of the type attributes were overridden from the base - // schema definition. If this type does not inherit from a base schema, - // this method returns true. - // An attribute could be the value of any of the constraints, default - // value of a parameter or any other data that may be specified in - // parameter type definition in and can be inherited from the base schema. - virtual bool HasOverriddenAttributes() const; - - // Type conversion methods. Used in lieu of RTTI and dynamic_cast<>. - virtual IntPropType* GetInt() { return nullptr; } - virtual IntPropType const* GetInt() const { return nullptr; } - virtual DoublePropType* GetDouble() { return nullptr; } - virtual DoublePropType const* GetDouble() const { return nullptr; } - virtual StringPropType* GetString() { return nullptr; } - virtual StringPropType const* GetString() const { return nullptr; } - virtual BooleanPropType* GetBoolean() { return nullptr; } - virtual BooleanPropType const* GetBoolean() const { return nullptr; } - virtual ObjectPropType* GetObject() { return nullptr; } - virtual ObjectPropType const* GetObject() const { return nullptr; } - virtual ArrayPropType* GetArray() { return nullptr; } - virtual ArrayPropType const* GetArray() const { return nullptr; } - - // Makes a full copy of this type definition. - virtual std::unique_ptr<PropType> Clone() const; - - // Creates an instance of associated value object. - virtual std::unique_ptr<PropValue> CreatePropValue(const base::Value& value, - ErrorPtr* error) const = 0; - - // Saves the parameter type definition as a JSON object. - // If |full_schema| is set to true, the full type definition is saved, - // otherwise only the overridden properties and attributes from the base - // schema is saved. That is, inherited properties and attributes are not - // saved. - // If it fails, returns "nullptr" and fills in the |error| with additional - // error information. - // |in_command_def| is set to true if the property type describes a - // GCD command parameter, otherwise it is for an object property. - // Command definitions handle required parameters differently (using - // "isRequired" property as opposed to "required" list for object properties). - virtual std::unique_ptr<base::Value> ToJson(bool full_schema, - bool in_command_def) const; - // Parses an JSON parameter type definition. Optional |base_schema| may - // specify the base schema type definition this type should be based upon. - // If not specified (nullptr), the parameter type is assumed to be a full - // definition and any omitted required properties are treated as an error. - // Returns true on success, otherwise fills in the |error| with additional - // error information. - virtual bool FromJson(const base::DictionaryValue* value, - const PropType* base_schema, - ErrorPtr* error); - // Helper function to load object schema from JSON. - virtual bool ObjectSchemaFromJson(const base::DictionaryValue* value, - const PropType* base_schema, - std::set<std::string>* processed_keys, - ErrorPtr* error) { - return true; - } - // Helper function to load type-specific constraints from JSON. - virtual bool ConstraintsFromJson(const base::DictionaryValue* value, - std::set<std::string>* processed_keys, - ErrorPtr* error) { - return true; - } - - // Additional helper static methods to help with converting a type enum - // value into a string and back. - using TypeMap = std::vector<std::pair<ValueType, std::string>>; - // Returns a list of value types and corresponding type names. - static const TypeMap& GetTypeMap(); - // Gets the type name string for the given type. - static std::string GetTypeStringFromType(ValueType type); - // Finds the type for the given type name. Returns true on success. - static bool GetTypeFromTypeString(const std::string& name, ValueType* type); - - // Creates an instance of PropType-derived class for the specified - // parameter type. - static std::unique_ptr<PropType> Create(ValueType type); - - // Adds a constraint to the type definition. - void AddConstraint(std::unique_ptr<Constraint> constraint); - // Removes a constraint of given type, if it exists. - void RemoveConstraint(ConstraintType constraint_type); - // Removes all constraints. - void RemoveAllConstraints(); - - // Finds a constraint of given type. Returns nullptr if not found. - const Constraint* GetConstraint(ConstraintType constraint_type) const; - Constraint* GetConstraint(ConstraintType constraint_type); - - // Validates the given value against all the constraints. - bool ValidateConstraints(const PropValue& value, ErrorPtr* error) const; - - protected: - friend class StatePackage; - virtual std::unique_ptr<PropValue> CreateDefaultValue() const = 0; - - // Specifies if this parameter definition is derived from a base - // object schema. - bool based_on_schema_ = false; - // A list of constraints specified for the parameter. - ConstraintMap constraints_; - // The default value specified for the parameter, if any. If the default - // value is present, the parameter is treated as optional and the default - // value is used if the parameter value is omitted when sending a command. - // Otherwise the parameter is treated as required and, if it is omitted, - // this is treated as an error. - InheritableAttribute<std::unique_ptr<PropValue>> default_; - // Specifies whether the parameter/property is required and must be specified - // (either directly, or by the default value being provided in the schema). - // Non-required parameters can be omitted completely and their values will not - // be present in the object instance. - InheritableAttribute<bool> required_; -}; - -// Base class for all the derived concrete implementations of property -// type classes. Provides implementations for common methods of PropType base. -template <class Derived, class Value, typename T> -class PropTypeBase : public PropType { - public: - // Overrides from PropType. - ValueType GetType() const override { return GetValueType<T>(); } - - std::unique_ptr<PropValue> CreatePropValue(const base::Value& value, - ErrorPtr* error) const override { - return CreateValue(value, error); - } - - std::unique_ptr<Value> CreateValue(const base::Value& value, - ErrorPtr* error) const { - return Value::CreateFromJson(value, *this, error); - } - - bool ConstraintsFromJson(const base::DictionaryValue* value, - std::set<std::string>* processed_keys, - ErrorPtr* error) override; - - protected: - std::unique_ptr<PropValue> CreateDefaultValue() const override { - if (GetDefaultValue()) - return GetDefaultValue()->Clone(); - return std::unique_ptr<PropValue>{new Value{*this}}; - } -}; - -// Helper base class for Int and Double parameter types. -template <class Derived, class Value, typename T> -class NumericPropTypeBase : public PropTypeBase<Derived, Value, T> { - public: - using Base = PropTypeBase<Derived, Value, T>; - bool ConstraintsFromJson(const base::DictionaryValue* value, - std::set<std::string>* processed_keys, - ErrorPtr* error) override; - - // Helper method to set and obtain a min/max constraint values. - // Used mostly for unit testing. - void AddMinMaxConstraint(T min_value, T max_value) { - InheritableAttribute<T> min_attr(min_value, false); - InheritableAttribute<T> max_attr(max_value, false); - this->AddConstraint( - std::unique_ptr<ConstraintMin<T>>{new ConstraintMin<T>{min_attr}}); - this->AddConstraint( - std::unique_ptr<ConstraintMax<T>>{new ConstraintMax<T>{max_attr}}); - } - T GetMinValue() const { - auto mmc = static_cast<const ConstraintMin<T>*>( - this->GetConstraint(ConstraintType::Min)); - return mmc ? mmc->limit_.value : std::numeric_limits<T>::lowest(); - } - T GetMaxValue() const { - auto mmc = static_cast<const ConstraintMax<T>*>( - this->GetConstraint(ConstraintType::Max)); - return mmc ? mmc->limit_.value : (std::numeric_limits<T>::max)(); - } -}; - -// Property definition of Integer type. -class IntPropType : public NumericPropTypeBase<IntPropType, IntValue, int> { - public: - // Overrides from the PropType base class. - IntPropType* GetInt() override { return this; } - IntPropType const* GetInt() const override { return this; } -}; - -// Property definition of Number type. -class DoublePropType - : public NumericPropTypeBase<DoublePropType, DoubleValue, double> { - public: - // Overrides from the PropType base class. - DoublePropType* GetDouble() override { return this; } - DoublePropType const* GetDouble() const override { return this; } -}; - -// Property definition of String type. -class StringPropType - : public PropTypeBase<StringPropType, StringValue, std::string> { - public: - using Base = PropTypeBase<StringPropType, StringValue, std::string>; - // Overrides from the PropType base class. - StringPropType* GetString() override { return this; } - StringPropType const* GetString() const override { return this; } - - bool ConstraintsFromJson(const base::DictionaryValue* value, - std::set<std::string>* processed_keys, - ErrorPtr* error) override; - - // Helper methods to add and inspect simple constraints. - // Used mostly for unit testing. - void AddLengthConstraint(int min_len, int max_len); - int GetMinLength() const; - int GetMaxLength() const; -}; - -// Property definition of Boolean type. -class BooleanPropType - : public PropTypeBase<BooleanPropType, BooleanValue, bool> { - public: - // Overrides from the PropType base class. - BooleanPropType* GetBoolean() override { return this; } - BooleanPropType const* GetBoolean() const override { return this; } -}; - -// Parameter definition of Object type. -class ObjectPropType - : public PropTypeBase<ObjectPropType, ObjectValue, ValueMap> { - public: - using Base = PropTypeBase<ObjectPropType, ObjectValue, ValueMap>; - ObjectPropType(); - - // Overrides from the ParamType base class. - bool HasOverriddenAttributes() const override; - - ObjectPropType* GetObject() override { return this; } - ObjectPropType const* GetObject() const override { return this; } - - std::unique_ptr<PropType> Clone() const override; - - std::unique_ptr<base::Value> ToJson(bool full_schema, - bool in_command_def) const override; - bool ObjectSchemaFromJson(const base::DictionaryValue* value, - const PropType* base_schema, - std::set<std::string>* processed_keys, - ErrorPtr* error) override; - - // Returns a schema for Object-type parameter. - inline const ObjectSchema* GetObjectSchemaPtr() const { - return object_schema_.value.get(); - } - void SetObjectSchema(std::unique_ptr<const ObjectSchema> schema); - - private: - InheritableAttribute<std::unique_ptr<const ObjectSchema>> object_schema_; -}; - -// Parameter definition of Array type. -class ArrayPropType - : public PropTypeBase<ArrayPropType, ArrayValue, ValueVector> { - public: - using Base = PropTypeBase<ArrayPropType, ArrayValue, ValueVector>; - ArrayPropType(); - - // Overrides from the ParamType base class. - bool HasOverriddenAttributes() const override; - - ArrayPropType* GetArray() override { return this; } - ArrayPropType const* GetArray() const override { return this; } - - std::unique_ptr<PropType> Clone() const override; - - std::unique_ptr<base::Value> ToJson(bool full_schema, - bool in_command_def) const override; - - bool ObjectSchemaFromJson(const base::DictionaryValue* value, - const PropType* base_schema, - std::set<std::string>* processed_keys, - ErrorPtr* error) override; - - // Returns a type for Array elements. - inline const PropType* GetItemTypePtr() const { - return item_type_.value.get(); - } - void SetItemType(std::unique_ptr<const PropType> item_type); - - private: - InheritableAttribute<std::unique_ptr<const PropType>> item_type_; -}; - -} // namespace weave - -#endif // LIBWEAVE_SRC_COMMANDS_PROP_TYPES_H_ diff --git a/src/commands/prop_values.cc b/src/commands/prop_values.cc deleted file mode 100644 index 6f30bee..0000000 --- a/src/commands/prop_values.cc +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/commands/prop_values.h" - -#include "src/commands/prop_types.h" - -namespace weave { - -PropValue::PropValue(const PropType& type) : type_{type.Clone()} {} - -PropValue::PropValue(const PropValue& other) : PropValue{*other.type_} {} - -PropValue::~PropValue() {} - -} // namespace weave diff --git a/src/commands/prop_values.h b/src/commands/prop_values.h deleted file mode 100644 index 960802b..0000000 --- a/src/commands/prop_values.h +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef LIBWEAVE_SRC_COMMANDS_PROP_VALUES_H_ -#define LIBWEAVE_SRC_COMMANDS_PROP_VALUES_H_ - -#include <map> -#include <memory> -#include <string> - -#include <weave/error.h> - -#include "src/commands/schema_utils.h" - -namespace base { -class Value; -class DictionaryValue; -} // namespace base - -namespace weave { - -// Enumeration to indicate supported command parameter types. -enum class ValueType { - Int, - Double, - String, - Boolean, - Object, - Array, -}; - -class PropValue; -class IntValue; -class DoubleValue; -class StringValue; -class BooleanValue; -class ObjectValue; -class ArrayValue; - -class PropType; - -// Helper methods to get the parameter type enum value for the given -// native C++ data representation. -// The generic GetValueType<T>() is undefined, however particular -// type specializations return the appropriate ValueType. -template <typename T> -ValueType GetValueType(); // Undefined. -template <> -inline ValueType GetValueType<int>() { - return ValueType::Int; -} -template <> -inline ValueType GetValueType<double>() { - return ValueType::Double; -} -template <> -inline ValueType GetValueType<std::string>() { - return ValueType::String; -} -template <> -inline ValueType GetValueType<bool>() { - return ValueType::Boolean; -} -template <> -inline ValueType GetValueType<ValueMap>() { - return ValueType::Object; -} -template <> -inline ValueType GetValueType<ValueVector>() { - return ValueType::Array; -} - -// The base class for property values. -// Concrete value classes of various types will be derived from this base. -// A property value is the actual command parameter value (or a concrete value -// that can be used in constraints and presets). The PropValue is mostly -// just parsed content of base::Value when a command is dispatched, however -// it does have some additional functionality: -// - it has a reference to the type definition (PropType) which is used -// when validating the value, especially for "object" types. -// - it can be compared with another instances of values of the same type. -// This is used to validate the values against "enum"/"one of" constraints. -class PropValue { - public: - // Only CreateDefaultValue should use this constructor. - explicit PropValue(const PropType& type); - virtual ~PropValue(); - - // Gets the type of the value. - virtual ValueType GetType() const = 0; - - // Type conversion methods. Used in lieu of RTTI and dynamic_cast<>. - virtual IntValue* GetInt() { return nullptr; } - virtual IntValue const* GetInt() const { return nullptr; } - virtual DoubleValue* GetDouble() { return nullptr; } - virtual DoubleValue const* GetDouble() const { return nullptr; } - virtual StringValue* GetString() { return nullptr; } - virtual StringValue const* GetString() const { return nullptr; } - virtual BooleanValue* GetBoolean() { return nullptr; } - virtual BooleanValue const* GetBoolean() const { return nullptr; } - virtual ObjectValue* GetObject() { return nullptr; } - virtual ObjectValue const* GetObject() const { return nullptr; } - virtual ArrayValue* GetArray() { return nullptr; } - virtual ArrayValue const* GetArray() const { return nullptr; } - - // Makes a full copy of this value class. - virtual std::unique_ptr<PropValue> Clone() const = 0; - - // Saves the value as a JSON object. Never fails. - virtual std::unique_ptr<base::Value> ToJson() const = 0; - - // Return the type definition of this value. - const PropType* GetPropType() const { return type_.get(); } - // Compares two values and returns true if they are equal. - virtual bool IsEqual(const PropValue* value) const = 0; - - protected: - // Special out-of-line constructor to help implement PropValue::Clone(). - // That method needs to clone the underlying type but can't do this in this - // header file since PropType is just forward-declared (it needs PropValue - // fully defined in its own inner workings). - explicit PropValue(const PropValue& other); - - private: - std::unique_ptr<const PropType> type_; -}; - -// A helper template base class for implementing value classes. -template <typename T> -class TypedValueBase : public PropValue { - public: - TypedValueBase(const PropType& type) : PropValue(type) {} - - // Overrides from PropValue base class. - ValueType GetType() const override { return GetValueType<T>(); } - - std::unique_ptr<base::Value> ToJson() const override { - return TypedValueToJson(value_); - } - - bool IsEqual(const PropValue* value) const override { - if (GetType() != value->GetType()) - return false; - return CompareValue(GetValue(), - static_cast<const TypedValueBase*>(value)->GetValue()); - } - - // Helper methods to get and set the C++ representation of the value. - const T& GetValue() const { return value_; } - - protected: - explicit TypedValueBase(const TypedValueBase& other) - : PropValue(other), value_(other.value_) {} - - TypedValueBase(const PropType& type, T value) - : PropValue(type), value_(value) {} - - private: - T value_{}; // The value of the parameter in C++ data representation. -}; - -// A helper template base class for implementing value classes. -template <typename Derived, typename T> -class TypedValueWithClone : public TypedValueBase<T> { - public: - TypedValueWithClone(const PropType& type) : TypedValueBase<T>(type) {} - TypedValueWithClone(const PropType& type, const T& value) - : TypedValueBase<T>(type, value) {} - - std::unique_ptr<PropValue> Clone() const override { - return std::unique_ptr<PropValue>{ - new Derived{*static_cast<const Derived*>(this)}}; - } - - static std::unique_ptr<Derived> CreateFromJson(const base::Value& value, - const PropType& type, - ErrorPtr* error) { - T tmp_value; - if (!TypedValueFromJson(&value, &type, &tmp_value, error)) - return nullptr; - - // Only place where invalid value can exist. - std::unique_ptr<Derived> result(new Derived(type, tmp_value)); - if (!result->GetPropType()->ValidateConstraints(*result, error)) - return nullptr; - - return result; - } -}; - -// Value of type Integer. -class IntValue final : public TypedValueWithClone<IntValue, int> { - public: - explicit IntValue(const PropType& type) - : TypedValueWithClone<IntValue, int>(type) {} - IntValue(const PropType& type, int value) - : TypedValueWithClone<IntValue, int>(type, value) {} - - IntValue* GetInt() override { return this; } - IntValue const* GetInt() const override { return this; } -}; - -// Value of type Number. -class DoubleValue final : public TypedValueWithClone<DoubleValue, double> { - public: - explicit DoubleValue(const PropType& type) - : TypedValueWithClone<DoubleValue, double>(type) {} - DoubleValue(const PropType& type, double value) - : TypedValueWithClone<DoubleValue, double>(type, value) {} - - DoubleValue* GetDouble() override { return this; } - DoubleValue const* GetDouble() const override { return this; } -}; - -// Value of type String. -class StringValue final : public TypedValueWithClone<StringValue, std::string> { - public: - explicit StringValue(const PropType& type) - : TypedValueWithClone<StringValue, std::string>(type) {} - StringValue(const PropType& type, std::string value) - : TypedValueWithClone<StringValue, std::string>(type, value) {} - - StringValue* GetString() override { return this; } - StringValue const* GetString() const override { return this; } -}; - -// Value of type Boolean. -class BooleanValue final : public TypedValueWithClone<BooleanValue, bool> { - public: - explicit BooleanValue(const PropType& type) - : TypedValueWithClone<BooleanValue, bool>(type) {} - BooleanValue(const PropType& type, bool value) - : TypedValueWithClone<BooleanValue, bool>(type, value) {} - - BooleanValue* GetBoolean() override { return this; } - BooleanValue const* GetBoolean() const override { return this; } -}; - -// Value of type Object. -class ObjectValue final : public TypedValueWithClone<ObjectValue, ValueMap> { - public: - explicit ObjectValue(const PropType& type) - : TypedValueWithClone<ObjectValue, ValueMap>(type) {} - ObjectValue(const PropType& type, ValueMap value) - : TypedValueWithClone<ObjectValue, ValueMap>(type, value) {} - - ObjectValue* GetObject() override { return this; } - ObjectValue const* GetObject() const override { return this; } -}; - -// Value of type Array. -class ArrayValue final : public TypedValueWithClone<ArrayValue, ValueVector> { - public: - explicit ArrayValue(const PropType& type) - : TypedValueWithClone<ArrayValue, ValueVector>(type) {} - ArrayValue(const PropType& type, ValueVector value) - : TypedValueWithClone<ArrayValue, ValueVector>(type, value) {} - - ArrayValue* GetArray() override { return this; } - ArrayValue const* GetArray() const override { return this; } -}; - -} // namespace weave - -#endif // LIBWEAVE_SRC_COMMANDS_PROP_VALUES_H_ diff --git a/src/commands/schema_constants.cc b/src/commands/schema_constants.cc index 7f8431f..cd8bf84 100644 --- a/src/commands/schema_constants.cc +++ b/src/commands/schema_constants.cc @@ -10,17 +10,9 @@ namespace errors { namespace commands { const char kDomain[] = "command_schema"; -const char kOutOfRange[] = "out_of_range"; const char kTypeMismatch[] = "type_mismatch"; -const char kPropTypeChanged[] = "param_type_changed"; -const char kUnknownType[] = "unknown_type"; -const char kInvalidPropDef[] = "invalid_parameter_definition"; const char kInvalidPropValue[] = "invalid_parameter_value"; -const char kNoTypeInfo[] = "no_type_info"; const char kPropertyMissing[] = "parameter_missing"; -const char kUnknownProperty[] = "unexpected_parameter"; -const char kInvalidObjectSchema[] = "invalid_object_schema"; -const char kDuplicateCommandDef[] = "duplicate_command_definition"; const char kInvalidCommandName[] = "invalid_command_name"; const char kCommandFailed[] = "command_failed"; const char kInvalidMinimalRole[] = "invalid_minimal_role"; @@ -32,25 +24,6 @@ const char kInvalidState[] = "invalid_state"; namespace commands { namespace attributes { -const char kType[] = "type"; -const char kDisplayName[] = "displayName"; -const char kDefault[] = "default"; -const char kItems[] = "items"; -const char kIsRequired[] = "isRequired"; - -const char kNumeric_Min[] = "minimum"; -const char kNumeric_Max[] = "maximum"; - -const char kString_MinLength[] = "minLength"; -const char kString_MaxLength[] = "maxLength"; - -const char kOneOf_Enum[] = "enum"; -const char kOneOf_Metadata[] = "metadata"; - -const char kObject_Properties[] = "properties"; -const char kObject_AdditionalProperties[] = "additionalProperties"; -const char kObject_Required[] = "required"; - const char kCommand_Id[] = "id"; const char kCommand_Name[] = "name"; const char kCommand_Parameters[] = "parameters"; diff --git a/src/commands/schema_constants.h b/src/commands/schema_constants.h index ea59f17..57766e6 100644 --- a/src/commands/schema_constants.h +++ b/src/commands/schema_constants.h @@ -13,17 +13,9 @@ namespace commands { extern const char kDomain[]; // Common command definition error codes. -extern const char kOutOfRange[]; extern const char kTypeMismatch[]; -extern const char kPropTypeChanged[]; -extern const char kUnknownType[]; -extern const char kInvalidPropDef[]; extern const char kInvalidPropValue[]; -extern const char kNoTypeInfo[]; extern const char kPropertyMissing[]; -extern const char kUnknownProperty[]; -extern const char kInvalidObjectSchema[]; -extern const char kDuplicateCommandDef[]; extern const char kInvalidCommandName[]; extern const char kCommandFailed[]; extern const char kInvalidMinimalRole[]; @@ -35,25 +27,6 @@ extern const char kInvalidState[]; namespace commands { namespace attributes { // Command description JSON schema attributes. -extern const char kType[]; -extern const char kDisplayName[]; -extern const char kDefault[]; -extern const char kItems[]; -extern const char kIsRequired[]; - -extern const char kNumeric_Min[]; -extern const char kNumeric_Max[]; - -extern const char kString_MinLength[]; -extern const char kString_MaxLength[]; - -extern const char kOneOf_Enum[]; -extern const char kOneOf_Metadata[]; - -extern const char kObject_Properties[]; -extern const char kObject_AdditionalProperties[]; -extern const char kObject_Required[]; - extern const char kCommand_Id[]; extern const char kCommand_Name[]; extern const char kCommand_Parameters[]; diff --git a/src/commands/schema_utils.cc b/src/commands/schema_utils.cc deleted file mode 100644 index be6cb16..0000000 --- a/src/commands/schema_utils.cc +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/commands/schema_utils.h" - -#include <algorithm> -#include <set> -#include <string> - -#include <base/json/json_writer.h> -#include <base/logging.h> - -#include "src/commands/object_schema.h" -#include "src/commands/prop_types.h" -#include "src/commands/prop_values.h" - -namespace weave { -namespace { -// Helper function to report "type mismatch" errors when parsing JSON. -void ReportJsonTypeMismatch(const tracked_objects::Location& location, - const base::Value* value_in, - const std::string& expected_type, - ErrorPtr* error) { - std::string value_as_string; - base::JSONWriter::Write(*value_in, &value_as_string); - Error::AddToPrintf(error, location, errors::commands::kDomain, - errors::commands::kTypeMismatch, - "Unable to convert value %s into %s", - value_as_string.c_str(), expected_type.c_str()); -} - -// Template version of ReportJsonTypeMismatch that deduces the type of expected -// data from the value_out parameter passed to particular overload of -// TypedValueFromJson() function. Always returns false. -template <typename T> -bool ReportUnexpectedJson(const tracked_objects::Location& location, - const base::Value* value_in, - T*, - ErrorPtr* error) { - ReportJsonTypeMismatch(location, value_in, - PropType::GetTypeStringFromType(GetValueType<T>()), - error); - return false; -} - -bool ErrorMissingProperty(ErrorPtr* error, - const tracked_objects::Location& location, - const char* param_name) { - Error::AddToPrintf(error, location, errors::commands::kDomain, - errors::commands::kPropertyMissing, - "Required parameter missing: %s", param_name); - return false; -} - -} // namespace - -// Specializations of TypedValueToJson<T>() for supported C++ types. -std::unique_ptr<base::FundamentalValue> TypedValueToJson(bool value) { - return std::unique_ptr<base::FundamentalValue>( - new base::FundamentalValue(value)); -} - -std::unique_ptr<base::FundamentalValue> TypedValueToJson(int value) { - return std::unique_ptr<base::FundamentalValue>( - new base::FundamentalValue(value)); -} - -std::unique_ptr<base::FundamentalValue> TypedValueToJson(double value) { - return std::unique_ptr<base::FundamentalValue>( - new base::FundamentalValue(value)); -} - -std::unique_ptr<base::StringValue> TypedValueToJson(const std::string& value) { - return std::unique_ptr<base::StringValue>(new base::StringValue(value)); -} - -std::unique_ptr<base::DictionaryValue> TypedValueToJson(const ValueMap& value) { - std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue); - for (const auto& pair : value) { - auto prop_value = pair.second->ToJson(); - CHECK(prop_value); - dict->SetWithoutPathExpansion(pair.first, prop_value.release()); - } - return dict; -} - -std::unique_ptr<base::ListValue> TypedValueToJson(const ValueVector& value) { - std::unique_ptr<base::ListValue> list(new base::ListValue); - for (const auto& item : value) { - auto json = item->ToJson(); - CHECK(json); - list->Append(json.release()); - } - return list; -} - -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - bool* value_out, - ErrorPtr* error) { - return value_in->GetAsBoolean(value_out) || - ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); -} - -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - int* value_out, - ErrorPtr* error) { - return value_in->GetAsInteger(value_out) || - ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); -} - -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - double* value_out, - ErrorPtr* error) { - return value_in->GetAsDouble(value_out) || - ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); -} - -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - std::string* value_out, - ErrorPtr* error) { - return value_in->GetAsString(value_out) || - ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); -} - -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - ValueMap* value_out, - ErrorPtr* error) { - const base::DictionaryValue* dict = nullptr; - if (!value_in->GetAsDictionary(&dict)) - return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); - - CHECK(type) << "Object definition must be provided"; - CHECK(ValueType::Object == type->GetType()) << "Type must be Object"; - - const ObjectSchema* object_schema = type->GetObject()->GetObjectSchemaPtr(); - std::set<std::string> keys_processed; - value_out->clear(); // Clear possible default values already in |value_out|. - for (const auto& pair : object_schema->GetProps()) { - const PropValue* def_value = pair.second->GetDefaultValue(); - if (dict->HasKey(pair.first)) { - const base::Value* param_value = nullptr; - CHECK(dict->GetWithoutPathExpansion(pair.first, ¶m_value)) - << "Unable to get parameter"; - auto value = pair.second->CreatePropValue(*param_value, error); - if (!value) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kInvalidPropValue, - "Invalid value for property '%s'", - pair.first.c_str()); - return false; - } - value_out->insert(std::make_pair(pair.first, std::move(value))); - keys_processed.insert(pair.first); - } else if (def_value) { - value_out->insert(std::make_pair(pair.first, def_value->Clone())); - keys_processed.insert(pair.first); - } else if (pair.second->IsRequired()) { - return ErrorMissingProperty(error, FROM_HERE, pair.first.c_str()); - } - } - - // Just for sanity, make sure that we processed all the necessary properties - // and there weren't any extra (unknown) ones specified. If so, ignore - // them, but log as warnings... - base::DictionaryValue::Iterator iter(*dict); - while (!iter.IsAtEnd()) { - std::string key = iter.key(); - if (keys_processed.find(key) == keys_processed.end() && - !object_schema->GetExtraPropertiesAllowed()) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kUnknownProperty, - "Unrecognized parameter '%s'", key.c_str()); - return false; - } - iter.Advance(); - } - - // Now go over all property values and validate them. - for (const auto& pair : *value_out) { - const PropType* prop_type = pair.second->GetPropType(); - CHECK(prop_type) << "Value property type must be available"; - if (!prop_type->ValidateConstraints(*pair.second, error)) { - Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, - errors::commands::kInvalidPropValue, - "Invalid value for property '%s'", pair.first.c_str()); - return false; - } - } - return true; -} - -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - ValueVector* value_out, - ErrorPtr* error) { - const base::ListValue* list = nullptr; - if (!value_in->GetAsList(&list)) - return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); - - CHECK(type) << "Array type definition must be provided"; - CHECK(ValueType::Array == type->GetType()) << "Type must be Array"; - const PropType* item_type = type->GetArray()->GetItemTypePtr(); - CHECK(item_type) << "Incomplete array type definition"; - - // This value might already contain values from the type defaults. - // Clear them first before proceeding. - value_out->clear(); - value_out->reserve(list->GetSize()); - for (const base::Value* item : *list) { - std::unique_ptr<PropValue> prop_value = - item_type->CreatePropValue(*item, error); - if (!prop_value) - return false; - value_out->push_back(std::move(prop_value)); - } - return true; -} - -// Compares two sets of key-value pairs from two Objects. -static bool obj_cmp(const ValueMap::value_type& v1, - const ValueMap::value_type& v2) { - return (v1.first == v2.first) && v1.second->IsEqual(v2.second.get()); -} - -bool operator==(const ValueMap& obj1, const ValueMap& obj2) { - if (obj1.size() != obj2.size()) - return false; - - auto pair = std::mismatch(obj1.begin(), obj1.end(), obj2.begin(), obj_cmp); - return pair == std::make_pair(obj1.end(), obj2.end()); -} - -bool operator==(const ValueVector& arr1, const ValueVector& arr2) { - if (arr1.size() != arr2.size()) - return false; - - using Type = const ValueVector::value_type; - // Compare two array items. - auto arr_cmp = [](const Type& v1, const Type& v2) { - return v1->IsEqual(v2.get()); - }; - auto pair = std::mismatch(arr1.begin(), arr1.end(), arr2.begin(), arr_cmp); - return pair == std::make_pair(arr1.end(), arr2.end()); -} - -std::string ToString(const ValueMap& obj) { - auto val = TypedValueToJson(obj); - std::string str; - base::JSONWriter::Write(*val, &str); - return str; -} - -std::string ToString(const ValueVector& arr) { - auto val = TypedValueToJson(arr); - std::string str; - base::JSONWriter::Write(*val, &str); - return str; -} - -} // namespace weave diff --git a/src/commands/schema_utils.h b/src/commands/schema_utils.h deleted file mode 100644 index 0c1d1b3..0000000 --- a/src/commands/schema_utils.h +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef LIBWEAVE_SRC_COMMANDS_SCHEMA_UTILS_H_ -#define LIBWEAVE_SRC_COMMANDS_SCHEMA_UTILS_H_ - -#include <cmath> -#include <limits> -#include <map> -#include <memory> -#include <string> -#include <type_traits> -#include <vector> - -#include <base/logging.h> -#include <base/values.h> -#include <weave/error.h> - -namespace weave { - -class PropType; -class PropValue; -class ObjectSchema; -class ObjectValue; - -// C++ representation of object values. -using ValueMap = std::map<std::string, std::shared_ptr<const PropValue>>; - -// C++ representation of array of values. -using ValueVector = std::vector<std::shared_ptr<const PropValue>>; - -// Converts an object to string. -std::string ToString(const ValueMap& obj); - -// Converts an array to string. -std::string ToString(const ValueVector& arr); - -// InheritableAttribute class is used for specifying various command parameter -// attributes that can be inherited from a base (parent) schema. -// The |value| still specifies the actual attribute values, whether it -// is inherited or overridden, while |is_inherited| can be used to identify -// if the attribute was inherited (true) or overridden (false). -template <typename T> -class InheritableAttribute final { - public: - InheritableAttribute() = default; - explicit InheritableAttribute(T val) - : value(std::move(val)), is_inherited(true) {} - InheritableAttribute(T val, bool inherited) - : value(std::move(val)), is_inherited(inherited) {} - T value{}; - bool is_inherited{true}; -}; - -// A bunch of helper function to create base::Value for specific C++ classes, -// including vectors of types. These are used in template classes below -// to simplify specialization logic. -std::unique_ptr<base::FundamentalValue> TypedValueToJson(bool value); -std::unique_ptr<base::FundamentalValue> TypedValueToJson(int value); -std::unique_ptr<base::FundamentalValue> TypedValueToJson(double value); -std::unique_ptr<base::StringValue> TypedValueToJson(const std::string& value); -std::unique_ptr<base::DictionaryValue> TypedValueToJson(const ValueMap& value); -std::unique_ptr<base::ListValue> TypedValueToJson(const ValueVector& value); -template <typename T> -std::unique_ptr<base::ListValue> TypedValueToJson( - const std::vector<T>& values) { - std::unique_ptr<base::ListValue> list(new base::ListValue); - for (const auto& v : values) { - auto json = TypedValueToJson(v); - CHECK(json); - list->Append(json.release()); - } - return list; -} - -// Similarly to TypedValueToJson() function above, the following overloaded -// helper methods allow to extract specific C++ data types from base::Value. -// Also used in template classes below to simplify specialization logic. -// TODO(vitalybuka): Fix this. Interface is misleading. Seeing PropType internal -// type validation is expected. In reality only ValueMap and ValueVector do -// validation. -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - bool* value_out, - ErrorPtr* error); -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - int* value_out, - ErrorPtr* error); -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - double* value_out, - ErrorPtr* error); -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - std::string* value_out, - ErrorPtr* error); -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - ValueMap* value_out, - ErrorPtr* error); -bool TypedValueFromJson(const base::Value* value_in, - const PropType* type, - ValueVector* value_out, - ErrorPtr* error); - -bool operator==(const ValueMap& obj1, const ValueMap& obj2); -bool operator==(const ValueVector& arr1, const ValueVector& arr2); - -// CompareValue is a helper function to help with implementing EqualsTo operator -// for various data types. For most scalar types it is using operator==(), -// however, for floating point values, rounding errors in binary representation -// of IEEE floats/doubles can cause straight == comparison to fail for seemingly -// equivalent values. For these, use approximate comparison with the error -// margin equal to the epsilon value defined for the corresponding data type. -// This is used when looking up values for implementation of OneOf constraints -// which should work reliably for floating points also ("number" type). - -// Compare exact types using ==. -template <typename T> -inline typename std::enable_if<!std::is_floating_point<T>::value, bool>::type -CompareValue(const T& v1, const T& v2) { - return v1 == v2; -} - -// Compare non-exact types (such as double) using precision margin (epsilon). -template <typename T> -inline typename std::enable_if<std::is_floating_point<T>::value, bool>::type -CompareValue(const T& v1, const T& v2) { - return std::abs(v1 - v2) <= std::numeric_limits<T>::epsilon(); -} - -} // namespace weave - -#endif // LIBWEAVE_SRC_COMMANDS_SCHEMA_UTILS_H_ diff --git a/src/commands/schema_utils_unittest.cc b/src/commands/schema_utils_unittest.cc deleted file mode 100644 index 1ea4c90..0000000 --- a/src/commands/schema_utils_unittest.cc +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/commands/schema_utils.h" - -#include <memory> -#include <string> -#include <vector> - -#include <base/values.h> -#include <gtest/gtest.h> - -#include "src/commands/object_schema.h" -#include "src/commands/prop_types.h" -#include "src/commands/prop_values.h" -#include "src/commands/schema_constants.h" -#include "src/commands/unittest_utils.h" - -namespace weave { - -using test::CreateDictionaryValue; -using test::CreateValue; - -TEST(CommandSchemaUtils, TypedValueToJson_Scalar) { - EXPECT_JSON_EQ("true", *TypedValueToJson(true)); - EXPECT_JSON_EQ("false", *TypedValueToJson(false)); - - EXPECT_JSON_EQ("0", *TypedValueToJson(0)); - EXPECT_JSON_EQ("-10", *TypedValueToJson(-10)); - EXPECT_JSON_EQ("20", *TypedValueToJson(20)); - - EXPECT_JSON_EQ("0.0", *TypedValueToJson(0.0)); - EXPECT_JSON_EQ("1.2", *TypedValueToJson(1.2)); - - EXPECT_JSON_EQ("'abc'", *TypedValueToJson(std::string("abc"))); - - std::vector<bool> bool_array{true, false}; - EXPECT_JSON_EQ("[true,false]", *TypedValueToJson(bool_array)); - - std::vector<int> int_array{1, 2, 5}; - EXPECT_JSON_EQ("[1,2,5]", *TypedValueToJson(int_array)); - - std::vector<double> dbl_array{1.1, 2.2}; - EXPECT_JSON_EQ("[1.1,2.2]", *TypedValueToJson(dbl_array)); - - std::vector<std::string> str_array{"a", "bc"}; - EXPECT_JSON_EQ("['a','bc']", *TypedValueToJson(str_array)); -} - -TEST(CommandSchemaUtils, TypedValueToJson_Object) { - IntPropType int_type; - ValueMap object; - - object.insert(std::make_pair( - "width", int_type.CreateValue(base::FundamentalValue{640}, nullptr))); - object.insert(std::make_pair( - "height", int_type.CreateValue(base::FundamentalValue{480}, nullptr))); - EXPECT_JSON_EQ("{'height':480,'width':640}", *TypedValueToJson(object)); -} - -TEST(CommandSchemaUtils, TypedValueToJson_Array) { - IntPropType int_type; - ValueVector arr; - - arr.push_back(int_type.CreateValue(base::FundamentalValue{640}, nullptr)); - arr.push_back(int_type.CreateValue(base::FundamentalValue{480}, nullptr)); - EXPECT_JSON_EQ("[640,480]", *TypedValueToJson(arr)); -} - -TEST(CommandSchemaUtils, TypedValueFromJson_Bool) { - bool value; - - EXPECT_TRUE( - TypedValueFromJson(CreateValue("true").get(), nullptr, &value, nullptr)); - EXPECT_TRUE(value); - - EXPECT_TRUE( - TypedValueFromJson(CreateValue("false").get(), nullptr, &value, nullptr)); - EXPECT_FALSE(value); - - ErrorPtr error; - EXPECT_FALSE( - TypedValueFromJson(CreateValue("0").get(), nullptr, &value, &error)); - EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); - error.reset(); -} - -TEST(CommandSchemaUtils, TypedValueFromJson_Int) { - int value; - - EXPECT_TRUE( - TypedValueFromJson(CreateValue("0").get(), nullptr, &value, nullptr)); - EXPECT_EQ(0, value); - - EXPECT_TRUE( - TypedValueFromJson(CreateValue("23").get(), nullptr, &value, nullptr)); - EXPECT_EQ(23, value); - - EXPECT_TRUE( - TypedValueFromJson(CreateValue("-1234").get(), nullptr, &value, nullptr)); - EXPECT_EQ(-1234, value); - - ErrorPtr error; - EXPECT_FALSE( - TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, &error)); - EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); - error.reset(); -} - -TEST(CommandSchemaUtils, TypedValueFromJson_Double) { - double value; - - EXPECT_TRUE( - TypedValueFromJson(CreateValue("0").get(), nullptr, &value, nullptr)); - EXPECT_DOUBLE_EQ(0.0, value); - EXPECT_TRUE( - TypedValueFromJson(CreateValue("0.0").get(), nullptr, &value, nullptr)); - EXPECT_DOUBLE_EQ(0.0, value); - - EXPECT_TRUE( - TypedValueFromJson(CreateValue("23").get(), nullptr, &value, nullptr)); - EXPECT_EQ(23.0, value); - EXPECT_TRUE( - TypedValueFromJson(CreateValue("23.1").get(), nullptr, &value, nullptr)); - EXPECT_EQ(23.1, value); - - EXPECT_TRUE(TypedValueFromJson(CreateValue("-1.23E+02").get(), nullptr, - &value, nullptr)); - EXPECT_EQ(-123.0, value); - - ErrorPtr error; - EXPECT_FALSE( - TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, &error)); - EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); - error.reset(); -} - -TEST(CommandSchemaUtils, TypedValueFromJson_String) { - std::string value; - - EXPECT_TRUE( - TypedValueFromJson(CreateValue("''").get(), nullptr, &value, nullptr)); - EXPECT_EQ("", value); - - EXPECT_TRUE( - TypedValueFromJson(CreateValue("'23'").get(), nullptr, &value, nullptr)); - EXPECT_EQ("23", value); - - EXPECT_TRUE( - TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, nullptr)); - EXPECT_EQ("abc", value); - - ErrorPtr error; - EXPECT_FALSE( - TypedValueFromJson(CreateValue("12").get(), nullptr, &value, &error)); - EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); - error.reset(); -} - -TEST(CommandSchemaUtils, TypedValueFromJson_Object) { - ValueMap value; - std::unique_ptr<ObjectSchema> schema{new ObjectSchema}; - - IntPropType age_prop; - age_prop.AddMinMaxConstraint(0, 150); - schema->AddProp("age", age_prop.Clone()); - - StringPropType name_prop; - name_prop.AddLengthConstraint(1, 30); - schema->AddProp("name", name_prop.Clone()); - - ObjectPropType type; - type.SetObjectSchema(std::move(schema)); - EXPECT_TRUE(TypedValueFromJson(CreateValue("{'age':20,'name':'Bob'}").get(), - &type, &value, nullptr)); - ValueMap value2; - value2.insert(std::make_pair( - "age", age_prop.CreateValue(base::FundamentalValue{20}, nullptr))); - value2.insert(std::make_pair( - "name", name_prop.CreateValue(base::StringValue("Bob"), nullptr))); - EXPECT_EQ(value2, value); - - ErrorPtr error; - EXPECT_FALSE( - TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, &error)); - EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode()); - error.reset(); -} - -TEST(CommandSchemaUtils, TypedValueFromJson_Array) { - ValueVector arr; - StringPropType str_type; - str_type.AddLengthConstraint(3, 100); - ArrayPropType type; - type.SetItemType(str_type.Clone()); - - EXPECT_TRUE(TypedValueFromJson(CreateValue("['foo', 'bar']").get(), &type, - &arr, nullptr)); - ValueVector arr2; - arr2.push_back(str_type.CreateValue(base::StringValue{"foo"}, nullptr)); - arr2.push_back(str_type.CreateValue(base::StringValue{"bar"}, nullptr)); - EXPECT_EQ(arr2, arr); - - ErrorPtr error; - EXPECT_FALSE(TypedValueFromJson(CreateValue("['baz', 'ab']").get(), &type, - &arr, &error)); - EXPECT_EQ(errors::commands::kOutOfRange, error->GetCode()); - error.reset(); -} - -} // namespace weave diff --git a/src/commands/unittest_utils.h b/src/commands/unittest_utils.h deleted file mode 100644 index 25392ee..0000000 --- a/src/commands/unittest_utils.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2015 The Weave Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef LIBWEAVE_SRC_COMMANDS_UNITTEST_UTILS_H_ -#define LIBWEAVE_SRC_COMMANDS_UNITTEST_UTILS_H_ - -#include <memory> -#include <string> - -#include <base/values.h> -#include <gtest/gtest.h> -#include <weave/test/unittest_utils.h> - -#include "src/commands/prop_types.h" -#include "src/commands/prop_values.h" - -namespace weave { -namespace test { - -template <typename T> -std::unique_ptr<const PropValue> make_prop_value(const base::Value& value) { - auto prop_type = PropType::Create(GetValueType<T>()); - return prop_type->CreatePropValue(value, nullptr); -} - -inline std::unique_ptr<const PropValue> make_int_prop_value(int value) { - return make_prop_value<int>(base::FundamentalValue{value}); -} - -inline std::unique_ptr<const PropValue> make_double_prop_value(double value) { - return make_prop_value<double>(base::FundamentalValue{value}); -} - -inline std::unique_ptr<const PropValue> make_bool_prop_value(bool value) { - return make_prop_value<bool>(base::FundamentalValue{value}); -} - -inline std::unique_ptr<const PropValue> make_string_prop_value( - const std::string& value) { - return make_prop_value<std::string>(base::StringValue{value}); -} - -} // namespace test -} // namespace weave - -#endif // LIBWEAVE_SRC_COMMANDS_UNITTEST_UTILS_H_ |