Commit a88c177e authored by Marcin Siodelski's avatar Marcin Siodelski

[#429] Updated StampedValue to support strings, integers, bool and real.

parent 90097f31
......@@ -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);
......
......@@ -11,48 +11,122 @@
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
......
// 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.
......
// 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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment