buffet: Add D-Bus serialization support for buffet Array types Added methods for converting native_types::Array to chromeos::Any, containing std::vector<T> and back. These are used to marshal ArrayPropValue over D-Bus when sending commands to vendor daemons for processing. BUG=brillo:107 TEST=`FEATURES=test emerge-link buffet` Change-Id: I44197ef9cf2379c298b081d7ce6522e6d22facfa Reviewed-on: https://chromium-review.googlesource.com/262206 Tested-by: Alex Vakulenko <avakulenko@chromium.org> Reviewed-by: Vitaly Buka <vitalybuka@chromium.org> Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/commands/prop_types.cc b/buffet/commands/prop_types.cc index 74e2be6..6365dcc 100644 --- a/buffet/commands/prop_types.cc +++ b/buffet/commands/prop_types.cc
@@ -520,6 +520,36 @@ return true; } +chromeos::Any ObjectPropType::ConvertArrayToDBusVariant( + const native_types::Array& source) const { + std::vector<chromeos::VariantDictionary> result; + result.reserve(source.size()); + for (const auto& prop_value : source) { + chromeos::Any dict = PropValueToDBusVariant(prop_value.get()); + result.push_back(std::move(*dict.GetPtr<chromeos::VariantDictionary>())); + } + return result; +} + +bool ObjectPropType::ConvertDBusVariantToArray( + const chromeos::Any& source, + native_types::Array* result, + chromeos::ErrorPtr* error) const { + if (!source.IsTypeCompatible<std::vector<chromeos::VariantDictionary>>()) + return GenerateErrorValueTypeMismatch(error); + + const auto& source_array = + source.Get<std::vector<chromeos::VariantDictionary>>(); + result->reserve(source_array.size()); + for (const auto& value : source_array) { + auto prop_value = PropValueFromDBusVariant(this, value, error); + if (!prop_value) + return false; + result->push_back(std::move(prop_value)); + } + return true; +} + void ObjectPropType::SetObjectSchema( std::unique_ptr<const ObjectSchema> schema) { object_schema_.value = std::move(schema);
diff --git a/buffet/commands/prop_types.h b/buffet/commands/prop_types.h index 473a6c8..4841db6 100644 --- a/buffet/commands/prop_types.h +++ b/buffet/commands/prop_types.h
@@ -89,6 +89,21 @@ virtual std::unique_ptr<PropValue> CreateValue( const chromeos::Any& val, chromeos::ErrorPtr* error) const = 0; + // Converts an array of PropValue containing the values of the types described + // by this instance of PropType into an Any containing std::vector<T>, where + // T corresponds to the native representation of this PropType. + virtual chromeos::Any ConvertArrayToDBusVariant( + const native_types::Array& source) const = 0; + + // ConvertAnyToArray is the opposite of ConvertArrayToAny(). + // Given an Any containing std::vector<T>, converts each value into the + // corresponding PropValue of type of this PropType and adds them to + // |result| array. If type conversion fails, this function returns false + // and specifies the error details in |error|. + virtual bool ConvertDBusVariantToArray(const chromeos::Any& source, + native_types::Array* result, + chromeos::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 @@ -184,12 +199,15 @@ 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> CreateValue() const override { if (GetDefaultValue()) return GetDefaultValue()->Clone(); return std::unique_ptr<PropValue>{new Value{Clone()}}; } + std::unique_ptr<PropValue> CreateValue( const chromeos::Any& v, chromeos::ErrorPtr* error) const override { std::unique_ptr<PropValue> prop_value; @@ -203,6 +221,34 @@ } return prop_value; } + + chromeos::Any ConvertArrayToDBusVariant( + const native_types::Array& source) const override { + std::vector<T> result; + result.reserve(source.size()); + for (const auto& prop_value : source) { + result.push_back(PropValueToDBusVariant(prop_value.get()).Get<T>()); + } + return result; + } + + bool ConvertDBusVariantToArray(const chromeos::Any& source, + native_types::Array* result, + chromeos::ErrorPtr* error) const override { + if (!source.IsTypeCompatible<std::vector<T>>()) + return GenerateErrorValueTypeMismatch(error); + + const auto& source_array = source.Get<std::vector<T>>(); + result->reserve(source_array.size()); + for (const auto& value : source_array) { + auto prop_value = PropValueFromDBusVariant(this, value, error); + if (!prop_value) + return false; + result->push_back(std::move(prop_value)); + } + return true; + } + bool ConstraintsFromJson(const base::DictionaryValue* value, std::set<std::string>* processed_keys, chromeos::ErrorPtr* error) override; @@ -307,6 +353,13 @@ std::set<std::string>* processed_keys, chromeos::ErrorPtr* error) override; + chromeos::Any ConvertArrayToDBusVariant( + const native_types::Array& source) const override; + + bool ConvertDBusVariantToArray(const chromeos::Any& source, + native_types::Array* result, + chromeos::ErrorPtr* error) const override; + // Returns a schema for Object-type parameter. inline const ObjectSchema* GetObjectSchemaPtr() const { return object_schema_.value.get();
diff --git a/buffet/commands/schema_utils.cc b/buffet/commands/schema_utils.cc index 06e5ded..47b757f 100644 --- a/buffet/commands/schema_utils.cc +++ b/buffet/commands/schema_utils.cc
@@ -267,9 +267,15 @@ } chromeos::Any PropValueToDBusVariant(const PropValue* value) { - if (value->GetType() != ValueType::Object) - return value->GetValueAsAny(); - return ObjectToDBusVariant(value->GetObject()->GetValue()); + if (value->GetType() == ValueType::Object) + return ObjectToDBusVariant(value->GetObject()->GetValue()); + + if (value->GetType() == ValueType::Array) { + const PropType* item_type = + value->GetPropType()->GetArray()->GetItemTypePtr(); + return item_type->ConvertArrayToDBusVariant(value->GetArray()->GetValue()); + } + return value->GetValueAsAny(); } chromeos::VariantDictionary @@ -291,13 +297,21 @@ const chromeos::Any& value, chromeos::ErrorPtr* error) { std::unique_ptr<const PropValue> result; - if (type->GetType() == ValueType::Object) { + if (type->GetType() == ValueType::Array) { + // Special case for array types. + // We expect the |value| to contain std::vector<T>, while PropValue must use + // native_types::Array instead. Do the conversion. + native_types::Array arr; + const PropType* item_type = type->GetArray()->GetItemTypePtr(); + if (item_type->ConvertDBusVariantToArray(value, &arr, error)) + result = type->CreateValue(arr, error); + } else if (type->GetType() == ValueType::Object) { // Special case for object types. // We expect the |value| to contain chromeos::VariantDictionary, while // PropValue must use native_types::Object instead. Do the conversion. if (!value.IsTypeCompatible<chromeos::VariantDictionary>()) { type->GenerateErrorValueTypeMismatch(error); - return {}; + return result; } CHECK(nullptr != type->GetObject()->GetObjectSchemaPtr()) << "An object type must have a schema defined for it"; @@ -305,8 +319,9 @@ if (!ObjectFromDBusVariant(type->GetObject()->GetObjectSchemaPtr(), value.Get<chromeos::VariantDictionary>(), &obj, - error)) - return {}; + error)) { + return result; + } result = type->CreateValue(std::move(obj), error); } else {
diff --git a/buffet/commands/schema_utils_unittest.cc b/buffet/commands/schema_utils_unittest.cc index 25fa553..8405722 100644 --- a/buffet/commands/schema_utils_unittest.cc +++ b/buffet/commands/schema_utils_unittest.cc
@@ -249,6 +249,18 @@ PropValueToDBusVariant(prop_value.get()).Get<VariantDictionary>(); EXPECT_EQ(20, dict["height"].Get<int>()); EXPECT_EQ(10, dict["width"].Get<int>()); + + buffet::ArrayPropType arr_type; + arr_type.SetItemType(str_type.Clone()); + buffet::native_types::Array arr; + arr.push_back(str_type.CreateValue(std::string{"foo"}, nullptr)); + arr.push_back(str_type.CreateValue(std::string{"bar"}, nullptr)); + arr.push_back(str_type.CreateValue(std::string{"baz"}, nullptr)); + prop_value = arr_type.CreateValue(arr, nullptr); + chromeos::Any any = PropValueToDBusVariant(prop_value.get()); + ASSERT_TRUE(any.IsTypeCompatible<std::vector<std::string>>()); + EXPECT_EQ((std::vector<std::string>{"foo", "bar", "baz"}), + any.Get<std::vector<std::string>>()); } TEST(CommandSchemaUtils, PropValueFromDBusVariant_Int) { @@ -340,3 +352,26 @@ ASSERT_NE(nullptr, error.get()); EXPECT_EQ(buffet::errors::commands::kOutOfRange, error->GetCode()); } + +TEST(CommandSchemaUtils, PropValueFromDBusVariant_Array) { + buffet::ArrayPropType arr_type; + buffet::IntPropType int_type; + int_type.AddMinMaxConstraint(0, 100); + arr_type.SetItemType(int_type.Clone()); + std::vector<int> data{0, 1, 1, 100}; + auto prop_value = PropValueFromDBusVariant(&arr_type, data, nullptr); + ASSERT_NE(nullptr, prop_value.get()); + auto arr = prop_value->GetValueAsAny().Get<buffet::native_types::Array>(); + ASSERT_EQ(4u, arr.size()); + EXPECT_EQ(0, arr[0]->GetInt()->GetValue()); + EXPECT_EQ(1, arr[1]->GetInt()->GetValue()); + EXPECT_EQ(1, arr[2]->GetInt()->GetValue()); + EXPECT_EQ(100, arr[3]->GetInt()->GetValue()); + + chromeos::ErrorPtr error; + data.push_back(-1); // This value is out of bounds for |int_type|. + prop_value = PropValueFromDBusVariant(&arr_type, data, &error); + EXPECT_EQ(nullptr, prop_value.get()); + ASSERT_NE(nullptr, error.get()); + EXPECT_EQ(buffet::errors::commands::kOutOfRange, error->GetCode()); +}