]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#429] Updated StampedValue to support strings, integers, bool and real.
authorMarcin Siodelski <marcin@isc.org>
Wed, 30 Jan 2019 17:28:58 +0000 (18:28 +0100)
committerMarcin Siodelski <marcin@isc.org>
Thu, 31 Jan 2019 17:07:35 +0000 (18:07 +0100)
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc
src/lib/cc/stamped_value.cc
src/lib/cc/stamped_value.h
src/lib/cc/tests/stamped_value_unittest.cc

index eb56ff7c1c543228e43b79ff42dccb9303b8e61a..f62a16f153f4a17340f501f97b1dcc9d7b133f78 100644 (file)
@@ -492,7 +492,8 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getAllGlobalParameters4) {
     cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(),
                                          StampedValue::create("name1", "value1"));
     cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(),
-                                         StampedValue::create("name2", 65));
+                                         StampedValue::create("name2",
+                                                              Element::create(static_cast<int64_t>(65))));
     cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(),
                                          StampedValue::create("name3", "value3"));
 
@@ -531,7 +532,7 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedGlobalParameters4) {
     cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(),
                                          value);
 
-    value = StampedValue::create("name2", 65);
+    value = StampedValue::create("name2", Element::create(static_cast<int64_t>(65)));
     value->setModificationTime(timestamps_["today"]);
     cbptr_->createUpdateGlobalParameter4(ServerSelector::ALL(),
                                          value);
index 6efb7f81e5233386b9f11f17802edf5315600bd8..25577388f894d486b355244ec36b35eb600d482c 100644 (file)
 namespace isc {
 namespace data {
 
-StampedValue::StampedValue(const std::string& name,
-                           const std::string& value)
+StampedValue::StampedValue(const std::string& name)
+    : StampedElement(), name_(name), value_() {
+}
+
+StampedValue::StampedValue(const std::string& name, const ElementPtr& value)
     : StampedElement(), name_(name), value_(value) {
+    validateConstruct();
 }
 
-StampedValue::StampedValue(const std::string& name,
-                           const int64_t value)
-    : StampedElement(), name_(name), value_() {
+StampedValue::StampedValue(const std::string& name, const std::string& value)
+    : StampedElement(), name_(name), value_(Element::create(value)) {
+    validateConstruct();
+}
 
-    try {
-        value_ = boost::lexical_cast<std::string>(value);
-    } catch (...) {
-        isc_throw(BadValue, "unable to cast value " << value
-                  << " to a string");
-    }
+StampedValuePtr
+StampedValue::create(const std::string& name) {
+    return (StampedValuePtr(new StampedValue(name)));
 }
 
 StampedValuePtr
-StampedValue::create(const std::string& name,
-                     const std::string& value) {
+StampedValue::create(const std::string& name, const ElementPtr& value) {
     return (StampedValuePtr(new StampedValue(name, value)));
 }
 
 StampedValuePtr
-StampedValue::create(const std::string& name,
-                     const int64_t value) {
+StampedValue::create(const std::string& name, const std::string& value) {
     return (StampedValuePtr(new StampedValue(name, value)));
 }
 
+int
+StampedValue::getType() const {
+    if (!value_) {
+        isc_throw(InvalidOperation, "StampedValue: attempt to retrieve the "
+                  "type of the null value for the '" << name_
+                  << "' parameter");
+    }
+
+    return (value_->getType());
+}
+
+std::string
+StampedValue::getValue() const {
+    validateAccess(Element::string);
+
+    try {
+        switch (static_cast<Element::types>(value_->getType())) {
+        case Element::string:
+            return (value_->stringValue());
+        case Element::integer:
+            return (boost::lexical_cast<std::string>(value_->intValue()));
+        case Element::boolean:
+            return (value_->boolValue() ? "1" : "0");
+        case Element::real:
+            return (boost::lexical_cast<std::string>(value_->doubleValue()));
+        default:
+            // Impossible condition.
+            isc_throw(TypeError, "StampedValue: invalid type of the '"
+                      << name_ << "' parameter");
+        }
+
+    } catch (const boost::bad_lexical_cast& ex) {
+        isc_throw(BadValue, "StampedValue: unable to convert the value of "
+                  "the parameter '" << name_ << "' to string");
+    }
+    return (value_->stringValue());
+}
 
 int64_t
 StampedValue::getSignedIntegerValue() const {
-    if (!value_.empty()) {
-        try {
-            return (boost::lexical_cast<int64_t>(value_));
-        } catch (...) {
-            isc_throw(BadValue, "unable to cast value " << value_
-                      << " to a signed integer");
-        }
+    validateAccess(Element::integer);
+    return (value_->intValue());
+}
+
+bool
+StampedValue::getBoolValue() const {
+    validateAccess(Element::boolean);
+    return (value_->boolValue());
+}
+
+double
+StampedValue::getDoubleValue() const {
+    validateAccess(Element::real);
+    return (value_->doubleValue());
+}
+
+void
+StampedValue::validateConstruct() const {
+    if (!value_) {
+        isc_throw(BadValue, "StampedValue: provided value of the '"
+                  << name_ << "' parameter is NULL");
+    }
+
+    if ((value_->getType() != Element::string) &&
+        (value_->getType() != Element::integer) &&
+        (value_->getType() != Element::boolean) &&
+        (value_->getType() != Element::real)) {
+        isc_throw(TypeError, "StampedValue: provided value of the '"
+                  << name_ << "' parameter has invalid type: "
+                  << Element::typeToName(static_cast<Element::types>(value_->getType())));
+    }
+}
+
+void
+StampedValue::validateAccess(Element::types type) const {
+    if (!value_) {
+        isc_throw(InvalidOperation, "StampedValue: attempt to get null value "
+                  "of the '" << name_ << "' parameter");
     }
 
-    return (0);
+    if ((type != Element::string) && (type != value_->getType())) {
+        isc_throw(TypeError, "StampedValue: attempt to access a '"
+                  << name_ << "' parameter as " << Element::typeToName(type)
+                  << ", but this parameter has "
+                  << Element::typeToName(static_cast<Element::types>(value_->getType()))
+                  << " type");
+    }
 }
 
 ElementPtr
index eda2f941551a10b36e1d2724022ac75df87f6a5e..0ffcdb7c9e293e9214c59f3a4829e0c33983f786 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -25,21 +25,38 @@ class StampedValue;
 /// @brief Pointer to the stamped value.
 typedef boost::shared_ptr<StampedValue> StampedValuePtr;
 
-/// @brief This class represents string or signed integer configuration
-/// element associated with the modification timestamp.
+/// @brief This class represents a named configuration parameter,
+/// e.g. global parameter of the DHCP server.
 ///
 /// Global configuration elements having simple types, e.g. DHCP
 /// timers, need to be associatied with modification timestamps.
 /// This association is made by deriving from @c StampedElement.
-/// Values can be both integers and strings. Because strings are
-/// more flexible, configuration elements are always held as strings
-/// in the configuration backends. This class reflects a single value
-/// held in the database. The value can be converted to an integer or
-/// can be returned as a string.
+/// The values can be strings, integers, booleans or real numbers.
+///
+/// Because the strings are more flexible, configuration elements
+/// are always held as strings in the configuration backends. This
+/// class reflects a single value held in the database. The value
+/// can be return in its orginal type or can be returned as a
+/// string. Also the null values are allowed.
 class StampedValue : public StampedElement {
 public:
 
-    /// @brief Constructor.
+    /// @brief Constructor creating a null value.
+    ///
+    /// @param name Name of the value.
+    StampedValue(const std::string& name);
+
+    /// @brief Constructor creating a value from the @c Element.
+    ///
+    /// @param name Name of the value.
+    /// @param value Value encapsulated in the @c Element object.
+    ///
+    /// @throw BadValue if the value is null.
+    /// @throw TypeError if the value is neither a string, integer,
+    /// bool nor real.
+    StampedValue(const std::string& name, const ElementPtr& value);
+
+    /// @brief Constructor creating a string value.
     ///
     /// Creates stamped value from a string.
     ///
@@ -47,41 +64,68 @@ public:
     /// @param value Value to be set.
     StampedValue(const std::string& name, const std::string& value);
 
-    /// @brief Constructor.
-    ///
-    /// Creates stamped value from the signed integer.
+    /// @brief Factory function creating a null value.
     ///
     /// @param name Name of the value.
-    /// @param value Value to be set.
-    explicit StampedValue(const std::string& name, const int64_t value);
+    static StampedValuePtr create(const std::string& name);
 
-    /// @brief Convenience function creating shared pointer to the object.
+    /// @brief Factory function creating a value from the @c Element.
     ///
     /// @param name Name of the value.
-    /// @param value String value to be encapsulated by this object.
+    /// @param value Value encapsulated in the @c Element object.
+    ///
+    /// @throw BadValue if the value is null.
+    /// @throw TypeError if the value is neither a string, integer,
+    /// bool nor real.
     static StampedValuePtr create(const std::string& name,
-                                  const std::string& value);
+                                  const ElementPtr& value);
 
-    /// @brief Convenience function creating shared Pointer to the object.
+    /// @brief Factory function creating a string value.
+    ///
+    /// Creates stamped value from a string.
     ///
     /// @param name Name of the value.
-    /// @param value Integer value to be encapsulated by this object.
+    /// @param value Value to be set.
     static StampedValuePtr create(const std::string& name,
-                                  const int64_t value);
+                                  const std::string& value);
+
+    /// @brief Returns a type of the value.
+    ///
+    /// @return Type of the value as integer. It can be compared
+    /// with the @c Element::getType() output.
+    /// @throw InvalidOperation if the value is null.
+    int getType() const;
 
     /// @brief Returns value name.
+    ///
+    /// @return Value name.
     std::string getName() const {
         return (name_);
     }
 
     /// @brief Returns value as string.
-    std::string getValue() const {
-        return (value_);
+    ///
+    /// It is allowed to call this function for all supported data
+    /// types. They are converted to a string. For example, a real
+    /// number of 1.4 will be returned as "1.4". The boolean true
+    /// value will be returned as "1" etc.
+    ///
+    /// @return Stored value as string.
+    /// @throw InvalidOperation if the value is null.
+    std::string getValue() const;
+
+    /// @brief Checks if the value is null.
+    ///
+    /// @return true if the value is null, false otherwise.
+    bool amNull() const {
+        return (!value_);
     }
 
     /// @brief Returns value as signed integer.
     ///
-    /// @throw BadValue if the value can't be converted to an integer.
+    /// @return Stored value as a signed integer.
+    /// @throw TypeError if the value is not of @c Element::integer
+    /// type.
     int64_t getSignedIntegerValue() const;
 
     /// @brief Creates an Element with the appropriate value
@@ -96,13 +140,49 @@ public:
     /// type is unsupported.
     ElementPtr toElement(const Element::types etype);
 
+    /// @brief Returns value as a boolean.
+    ///
+    /// @return Stored value as a boolean.
+    /// @throw TypeError if the value is not of @c Element::boolean
+    /// type.
+    bool getBoolValue() const;
+
+    /// @brief Returns value as a real number.
+    ///
+    /// @return Stored value as a real number.
+    /// @throw TypeError if the value is not of @c Element::real
+    /// type.
+    double getDoubleValue() const;
+
 private:
 
+    /// @brief Checks if the values passed to the constructors
+    /// were correct.
+    ///
+    /// This is called from the constructors.
+    ///
+    /// @throw BadValue if the value is null.
+    /// @throw TypeError if the value type is neither a string,
+    /// integer, boolean nor real.
+    void validateConstruct() const;
+
+    /// @brief Checks if the value is accessed correctly.
+    ///
+    /// This is called from the accessors of this class.
+    ///
+    /// @param type Type of the value expected by the accessor
+    /// function.
+    ///
+    /// @throw InvalidOperation if the accessed value is null.
+    /// @throw TypeError if the expected type is not a string
+    /// and it doesn't match the value type.
+    void validateAccess(Element::types type) const;
+
     /// @brief Name of the value.
     std::string name_;
 
-    /// @brief Holds value as a string.
-    std::string value_;
+    /// @brief Stored value.
+    ElementPtr value_;
 };
 
 /// @name Definition of the multi index container for @c StampedValue.
index 869ba4131009301375630d1fd59ae154a44b9d87..0fddf0d8a892d0a5ee2f4838b192a32d09441479 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -16,24 +16,87 @@ using namespace isc::data;
 
 namespace {
 
+// Tests that the stamped value can be created with a NULL value.
+TEST(StampedValueTest, createNull) {
+    StampedValuePtr value;
+    ASSERT_NO_THROW(value = StampedValue::create("bar"));
+
+    EXPECT_TRUE(value->amNull());
+
+    EXPECT_THROW(value->getType(), InvalidOperation);
+    EXPECT_THROW(value->getValue(), InvalidOperation);
+    EXPECT_THROW(value->getSignedIntegerValue(), InvalidOperation);
+    EXPECT_THROW(value->getBoolValue(), InvalidOperation);
+    EXPECT_THROW(value->getDoubleValue(), InvalidOperation);
+}
+
 // Tests that stamped value from string can be created.
 TEST(StampedValueTest, createFromString) {
-    boost::scoped_ptr<StampedValue> value;
-    ASSERT_NO_THROW(value.reset(new StampedValue("bar", "foo")));
+    StampedValuePtr value;
+    ASSERT_NO_THROW(value = StampedValue::create("bar", Element::create("foo")));
+    EXPECT_FALSE(value->amNull());
+    EXPECT_EQ(Element::string, value->getType());
     EXPECT_EQ("bar", value->getName());
     EXPECT_EQ("foo", value->getValue());
-    EXPECT_THROW(value->getSignedIntegerValue(), BadValue);
+
+    EXPECT_THROW(value->getSignedIntegerValue(), TypeError);
+    EXPECT_THROW(value->getBoolValue(), TypeError);
+    EXPECT_THROW(value->getDoubleValue(), TypeError);
 }
 
 // Tests that stamped value from integer can be created.
 TEST(StampedValueTest, createFromInteger) {
-    boost::scoped_ptr<StampedValue> value;
-    ASSERT_NO_THROW(value.reset(new StampedValue("bar", 5)));
+    StampedValuePtr value;
+    ASSERT_NO_THROW(value = StampedValue::create("bar", Element::create(static_cast<int64_t>(5))));
+    EXPECT_FALSE(value->amNull());
+    EXPECT_EQ(Element::integer, value->getType());
     EXPECT_EQ("bar", value->getName());
     EXPECT_EQ("5", value->getValue());
     int64_t signed_integer;
     ASSERT_NO_THROW(signed_integer = value->getSignedIntegerValue());
     EXPECT_EQ(5, signed_integer);
+
+    EXPECT_THROW(value->getBoolValue(), TypeError);
+    EXPECT_THROW(value->getDoubleValue(), TypeError);
+}
+
+// Tests that stamped value from bool can be created.
+TEST(StampedValueTest, createFromBool) {
+    StampedValuePtr value;
+    ASSERT_NO_THROW(value = StampedValue::create("bar", Element::create(static_cast<bool>(true))));
+    EXPECT_FALSE(value->amNull());
+    EXPECT_EQ(Element::boolean, value->getType());
+    EXPECT_EQ("bar", value->getName());
+    EXPECT_EQ("1", value->getValue());
+    bool bool_value = false;
+    ASSERT_NO_THROW(bool_value = value->getBoolValue());
+    EXPECT_TRUE(bool_value);
+
+    EXPECT_THROW(value->getSignedIntegerValue(), TypeError);
+    EXPECT_THROW(value->getDoubleValue(), TypeError);
+}
+
+// Tests that stamped value from real can be created.
+TEST(StampedValueTest, createFromDouble) {
+    StampedValuePtr value;
+    ASSERT_NO_THROW(value = StampedValue::create("bar", Element::create(static_cast<double>(1.45))));
+    EXPECT_FALSE(value->amNull());
+    EXPECT_EQ(Element::real, value->getType());
+    EXPECT_EQ("bar", value->getName());
+    EXPECT_EQ("1.45", value->getValue());
+    double double_value = 0;
+    ASSERT_NO_THROW(double_value = value->getDoubleValue());
+    EXPECT_EQ(1.45, double_value);
+
+    EXPECT_THROW(value->getSignedIntegerValue(), TypeError);
+    EXPECT_THROW(value->getBoolValue(), TypeError);
+}
+
+// Tests that the value must have an allowed type.
+TEST(StampedValueTest, createFailures) {
+    EXPECT_THROW(StampedValue::create("bar", ElementPtr()), BadValue);
+    EXPECT_THROW(StampedValue::create("bar", Element::createMap()), TypeError);
+    EXPECT_THROW(StampedValue::create("bar", Element::createList()), TypeError);
 }
 
 // Tests that Elements can be created from stamped values.