From 87da45dfc81913fa60a477d0d3402ee5030f602f Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Fri, 13 Aug 2021 02:05:32 +0200
Subject: [PATCH] WIP: consolidation

---
 include/CMakeLists.txt           |   2 +
 include/caosdb/data_type.h       | 101 +++++++++++++++++++++++++++++++
 include/caosdb/entity.h          |  77 ++++++++++++++++-------
 include/caosdb/protobuf_helper.h |  16 +++++
 include/caosdb/status_code.h     |   2 +
 include/caosdb/value.h           |  94 ++++++++++++++++++++++++++++
 proto                            |   2 +-
 src/caosdb/entity.cpp            |  72 ++++++++++++++++------
 test/test_entity.cpp             |  56 +++++++++--------
 test/test_transaction.cpp        |   2 +-
 10 files changed, 358 insertions(+), 66 deletions(-)
 create mode 100644 include/caosdb/data_type.h
 create mode 100644 include/caosdb/value.h

diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 542ad97..77d293d 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -25,6 +25,7 @@ set(libcaosdb_INCL
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/configuration.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/connection.h
     ${CMAKE_CURRENT_BINARY_DIR}/caosdb/constants.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/data_type.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/entity.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/exceptions.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/info.h
@@ -36,6 +37,7 @@ set(libcaosdb_INCL
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction_status.h
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/utility.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/value.h
     )
 
 # pass variable to parent scope
diff --git a/include/caosdb/data_type.h b/include/caosdb/data_type.h
new file mode 100644
index 0000000..194a2a6
--- /dev/null
+++ b/include/caosdb/data_type.h
@@ -0,0 +1,101 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CAOSDB_DATA_TYPE_H
+#define CAOSDB_DATA_TYPE_H
+#include <string>                           // for string
+#include "caosdb/protobuf_helper.h"         // for ProtoMessageWrapper
+#include "caosdb/entity/v1alpha1/main.pb.h" // for RepeatedPtrField, Message
+namespace caosdb::entity {
+using ProtoAtomicDataType = caosdb::entity::v1alpha1::AtomicDataType;
+using ProtoDataType = caosdb::entity::v1alpha1::DataType;
+using ProtoReferenceDataType = caosdb::entity::v1alpha1::ReferenceDataType;
+using DataTypeCase = caosdb::entity::v1alpha1::DataType::DataTypeCase;
+using caosdb::utility::ProtoMessageWrapper;
+
+// Atomic data types.
+enum AtomicDataType {
+  // The data type is unset/unknown.
+  UNSPECIFIED_DATA_TYPE = ProtoAtomicDataType::ATOMIC_DATA_TYPE_UNSPECIFIED,
+  // TEXT data type.
+  TEXT = ProtoAtomicDataType::ATOMIC_DATA_TYPE_TEXT,
+  // DOUBLE data type.
+  DOUBLE = ProtoAtomicDataType::ATOMIC_DATA_TYPE_DOUBLE,
+  // DATETIME data type.
+  DATETIME = ProtoAtomicDataType::ATOMIC_DATA_TYPE_DATETIME,
+  // INTEGER data type.
+  INTEGER = ProtoAtomicDataType::ATOMIC_DATA_TYPE_INTEGER,
+  // BOOLEAN data type.
+  BOOLEAN = ProtoAtomicDataType::ATOMIC_DATA_TYPE_BOOLEAN,
+};
+
+class DataType;
+
+class ReferenceDataType : ProtoMessageWrapper<ProtoReferenceDataType> {
+public:
+  [[nodiscard]] inline auto GetName() const noexcept -> const std::string & {
+    return this->wrapped->name();
+  }
+
+  ReferenceDataType() : ProtoMessageWrapper<ProtoReferenceDataType>() {}
+
+  friend class DataType;
+};
+
+class DataType : public ProtoMessageWrapper<ProtoDataType> {
+public:
+  DataType() : ProtoMessageWrapper<ProtoDataType>() {}
+  DataType(const std::string &data_type) : DataType() {
+    this->wrapped->mutable_reference_type()->set_name(data_type);
+  }
+  [[nodiscard]] inline auto IsAtomic() const noexcept -> bool {
+    return this->wrapped->data_type_case() == DataTypeCase::kAtomicType;
+  }
+  [[nodiscard]] inline auto AsAtomic() const noexcept -> AtomicDataType {
+    return static_cast<AtomicDataType>(this->wrapped->atomic_type());
+  }
+
+  [[nodiscard]] inline auto IsReference() const noexcept -> bool {
+    return this->wrapped->data_type_case() == DataTypeCase::kReferenceType;
+  }
+  [[nodiscard]] inline auto AsReference() const noexcept -> ReferenceDataType {
+    if (!IsReference()) {
+      return ReferenceDataType{};
+    } else if (reference_data_type == nullptr) {
+      reference_data_type = std::make_unique<ReferenceDataType>();
+      this->reference_data_type->wrapped =
+        this->wrapped->mutable_reference_type();
+    }
+    return *reference_data_type;
+  }
+
+  inline auto operator==(const DataType &other) const noexcept -> bool {
+    return this->wrapped->SerializeAsString() ==
+           other.wrapped->SerializeAsString();
+  }
+
+private:
+  mutable std::unique_ptr<ReferenceDataType> reference_data_type;
+};
+
+} // namespace caosdb::entity
+
+#endif
diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h
index bbccf11..0a66286 100644
--- a/include/caosdb/entity.h
+++ b/include/caosdb/entity.h
@@ -29,10 +29,13 @@
 #ifndef CAOSDB_ENTITY_H
 #define CAOSDB_ENTITY_H
 
-#include <string>                           // for string
+#include "caosdb/data_type.h"               // for DataType
 #include "caosdb/entity/v1alpha1/main.pb.h" // for RepeatedPtrField, Message
 #include "caosdb/message_code.h"            // for get_message_code, Messag...
-#include "google/protobuf/util/json_util.h" // for MessageToJsonString, Jso...
+#include "caosdb/status_code.h"             // for StatusCode
+#include "caosdb/value.h"                   // for Value
+#include <google/protobuf/util/json_util.h> // for MessageToJsonString, Jso...
+#include <string>                           // for string
 
 namespace caosdb::entity {
 using caosdb::entity::v1alpha1::IdResponse;
@@ -41,16 +44,31 @@ using ProtoProperty = caosdb::entity::v1alpha1::Property;
 using ProtoEntity = caosdb::entity::v1alpha1::Entity;
 using ProtoMessage = caosdb::entity::v1alpha1::Message;
 using caosdb::entity::v1alpha1::EntityRole;
+using ProtoImportance = caosdb::entity::v1alpha1::Importance;
+
+/**
+ * The property importance.
+ */
+enum Importance {
+  IMPORTANCE_UNSPECIFIED =
+    ProtoImportance::IMPORTANCE_UNSPECIFIED, ///< Unset/None
+  OBLIGATORY =
+    ProtoImportance::IMPORTANCE_OBLIGATORY, ///< Obligatory importance.
+  RECOMMENDED =
+    ProtoImportance::IMPORTANCE_RECOMMENDED, ///< Recommended importance.
+  SUGGESTED = ProtoImportance::IMPORTANCE_SUGGESTED, ///< Suggested importance.
+  FIX = ProtoImportance::IMPORTANCE_FIX,             ///< Fix importance.
+};
 
 /**
  * The entity role.
  */
 enum Role {
-  NONE = EntityRole::ENTITY_ROLE_UNSPECIFIED,        ///< Unset/None
-  RECORD_TYPE = EntityRole::ENTITY_ROLE_RECORD_TYPE, ///< RecordType
-  RECORD = EntityRole::ENTITY_ROLE_RECORD,           ///< Record
-  PROPERTY = EntityRole::ENTITY_ROLE_PROPERTY,       ///< Property
-  FILE = EntityRole::ENTITY_ROLE_FILE,               ///< File
+  ROLE_UNSPECIFIED = EntityRole::ENTITY_ROLE_UNSPECIFIED, ///< Unset/None
+  RECORD_TYPE = EntityRole::ENTITY_ROLE_RECORD_TYPE,      ///< RecordType
+  RECORD = EntityRole::ENTITY_ROLE_RECORD,                ///< Record
+  PROPERTY = EntityRole::ENTITY_ROLE_PROPERTY,            ///< Property
+  FILE = EntityRole::ENTITY_ROLE_FILE,                    ///< File
 };
 
 /**
@@ -268,11 +286,11 @@ public:
   /**
    * Return the importance of this  property
    */
-  [[nodiscard]] auto GetImportance() const -> const std::string &;
+  [[nodiscard]] auto GetImportance() const -> Importance;
   /**
    * Return the value of this  property
    */
-  [[nodiscard]] auto GetValue() const -> const std::string &;
+  [[nodiscard]] auto GetValue() const -> const Value &;
   /**
    * Return the unit of this  property
    */
@@ -280,7 +298,7 @@ public:
   /**
    * Return the datatype of this  property
    */
-  [[nodiscard]] auto GetDatatype() const -> const std::string &;
+  [[nodiscard]] auto GetDataType() const -> const DataType &;
   // TODO(fspreck) Implement these when we have decided how to attach
   // messages to properties.
   // [[nodiscard]] auto GetErrors() const -> const Messages &;
@@ -302,11 +320,15 @@ public:
   /**
    * Set the importance of this property.
    */
-  auto SetImportance(const std::string &importance) -> void;
+  auto SetImportance(Importance importance) -> void;
   /**
    * Set the value of this property.
    */
-  auto SetValue(const std::string &value) -> void;
+  auto SetValue(const Value &value) -> StatusCode;
+  auto SetValue(const std::string &value) -> StatusCode;
+  auto SetValue(const double value) -> StatusCode;
+  // auto SetValue(const int64_t value) -> StatusCode;
+  // auto SetValue(const bool value) -> StatusCode;
   /**
    * Set the unit of this property.
    */
@@ -314,7 +336,8 @@ public:
   /**
    * Set the datatype of this property.
    */
-  auto SetDatatype(const std::string &datatype) -> void;
+  auto SetDataType(const DataType &new_data_type) -> StatusCode;
+  auto SetDataType(const std::string &new_data_type) -> StatusCode;
 
   /**
    * Return a json string representing this property.
@@ -335,6 +358,8 @@ public:
 
 private:
   static auto CreateProtoProperty() -> ProtoProperty *;
+  Value value;
+  DataType data_type;
 
   mutable ProtoProperty *wrapped;
 };
@@ -405,8 +430,8 @@ public:
     return wrapped->version().id();
   };
 
-  [[nodiscard]] inline auto GetRole() const -> Role & {
-    return wrapped->role();
+  [[nodiscard]] inline auto GetRole() const -> Role {
+    return static_cast<Role>(wrapped->role());
   };
   [[nodiscard]] inline auto GetName() const -> const std::string & {
     return wrapped->name();
@@ -415,14 +440,14 @@ public:
     return wrapped->description();
   };
 
-  [[nodiscard]] inline auto GetDatatype() const -> const std::string & {
-    return wrapped->datatype();
+  [[nodiscard]] inline auto GetDataType() const -> const DataType & {
+    return this->data_type;
   };
   [[nodiscard]] inline auto GetUnit() const -> const std::string & {
     return wrapped->unit();
   };
-  [[nodiscard]] inline auto GetValue() const -> const std::string & {
-    return wrapped->value();
+  [[nodiscard]] inline auto GetValue() const -> const Value & {
+    return this->value;
   };
 
   [[nodiscard]] auto GetParents() const -> const Parents &;
@@ -456,10 +481,16 @@ public:
    */
   auto SetDescription(const std::string &description) -> void;
 
-  auto SetValue(const std::string &value) -> void;
+  auto SetValue(const Value &value) -> StatusCode;
+  auto SetValue(const std::string &value) -> StatusCode;
+  auto SetValue(const double value) -> StatusCode;
+  // auto SetValue(const int64_t value) -> StatusCode;
+  // auto SetValue(const bool value) -> StatusCode;
+
   auto SetUnit(const std::string &unit) -> void;
   // Currently no references or lists.
-  auto SetDatatype(const std::string &datatype) -> void;
+  auto SetDataType(const DataType &new_data_type) -> StatusCode;
+  auto SetDataType(const std::string &new_data_type) -> StatusCode;
   auto AppendProperty(const Property &property) -> void;
 
   auto AppendParent(const Parent &parent) -> void;
@@ -478,6 +509,10 @@ protected:
   Messages errors;
   Messages warnings;
   Messages infos;
+
+private:
+  Value value;
+  DataType data_type;
 };
 
 } // namespace caosdb::entity
diff --git a/include/caosdb/protobuf_helper.h b/include/caosdb/protobuf_helper.h
index ce7dd5e..e4e89ae 100644
--- a/include/caosdb/protobuf_helper.h
+++ b/include/caosdb/protobuf_helper.h
@@ -22,6 +22,7 @@
 #ifndef CAOSDB_PROTOBUF_HELPER_H
 #define CAOSDB_PROTOBUF_HELPER_H
 
+#include "caosdb/status_code.h"
 #include <google/protobuf/arena.h>
 
 namespace caosdb::utility {
@@ -30,5 +31,20 @@ using google::protobuf::Arena;
 
 auto get_arena() -> Arena *;
 
+template <typename P> class ProtoMessageWrapper {
+public:
+  inline auto CopyFrom(const ProtoMessageWrapper &other) noexcept
+    -> StatusCode {
+    this->wrapped->CopyFrom(*other.wrapped);
+    return StatusCode::SUCCESS;
+  }
+
+protected:
+  ProtoMessageWrapper() {
+    this->wrapped = Arena::CreateMessage<P>(get_arena());
+  }
+  P *wrapped;
+};
+
 } // namespace caosdb::utility
 #endif
diff --git a/include/caosdb/status_code.h b/include/caosdb/status_code.h
index a6fcda9..b138bbe 100644
--- a/include/caosdb/status_code.h
+++ b/include/caosdb/status_code.h
@@ -52,6 +52,8 @@ enum StatusCode {
   TRANSACTION_TYPE_ERROR = 26,
   UNSUPPORTED_FEATURE = 27,
   ORIGINAL_ENTITY_MISSING_ID = 28,
+  ENTITY_CANNOT_HAVE_A_DATA_TYPE = 30,
+  ENTITY_CANNOT_HAVE_A_VALUE = 30,
 };
 
 auto get_status_description(int code) -> const std::string &;
diff --git a/include/caosdb/value.h b/include/caosdb/value.h
new file mode 100644
index 0000000..158a3e8
--- /dev/null
+++ b/include/caosdb/value.h
@@ -0,0 +1,94 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CAOSDB_VALUE_H
+#define CAOSDB_VALUE_H
+#include <string>                           // for string
+#include "caosdb/protobuf_helper.h"         // for ProtoMessageWrapper
+#include "caosdb/entity/v1alpha1/main.pb.h" // for RepeatedPtrField, Message
+namespace caosdb::entity {
+using caosdb::utility::ProtoMessageWrapper;
+using ProtoSpecialValue = caosdb::entity::v1alpha1::SpecialValue;
+using ProtoValue = caosdb::entity::v1alpha1::Value;
+using ValueCase = caosdb::entity::v1alpha1::Value::ValueCase;
+using ScalarValueCase = caosdb::entity::v1alpha1::ScalarValue::ScalarValueCase;
+
+// Represents special values which are otherwise hard to tranfer via protobuf.
+enum SpecialValue {
+  // Represent the NULL value.
+  NULL_VALUE = ProtoSpecialValue::SPECIAL_VALUE_UNSPECIFIED,
+  // The empty string.
+  EMPTY_STRING = ProtoSpecialValue::SPECIAL_VALUE_EMPTY_STRING,
+};
+
+class Value : public ProtoMessageWrapper<ProtoValue> {
+public:
+  inline Value() : ProtoMessageWrapper<ProtoValue>() {
+    // has NULL_VALUE now
+  }
+  explicit inline Value(const std::string &value)
+    : ProtoMessageWrapper<ProtoValue>() {
+    this->wrapped->mutable_scalar_value()->set_string_value(value);
+  }
+  explicit inline Value(double value) : ProtoMessageWrapper<ProtoValue>() {
+    this->wrapped->mutable_scalar_value()->set_double_value(value);
+  }
+  explicit inline Value(const char value[]) : Value(std::string(value)) {}
+
+  [[nodiscard]] inline auto IsString() const noexcept -> bool {
+    if (this->wrapped->value_case() == ValueCase::kScalarValue) {
+
+      return (this->wrapped->scalar_value().scalar_value_case() ==
+              ScalarValueCase::kStringValue) ||
+             (this->wrapped->scalar_value().scalar_value_case() ==
+                ScalarValueCase::kSpecialValue &&
+              this->wrapped->scalar_value().special_value() ==
+                ProtoSpecialValue::SPECIAL_VALUE_EMPTY_STRING);
+    }
+    return false;
+  }
+  [[nodiscard]] inline auto AsString() const noexcept -> const std::string & {
+    return this->wrapped->scalar_value().string_value();
+    ;
+  }
+
+  [[nodiscard]] inline auto IsDouble() const noexcept -> bool {
+    if (this->wrapped->value_case() == ValueCase::kScalarValue) {
+
+      return (this->wrapped->scalar_value().scalar_value_case() ==
+              ScalarValueCase::kDoubleValue);
+    }
+    return false;
+  }
+  [[nodiscard]] inline auto AsDouble() const noexcept -> double {
+    return this->wrapped->scalar_value().double_value();
+    ;
+  }
+
+  inline auto operator==(const Value &other) const noexcept -> bool {
+    return this->wrapped->SerializeAsString() ==
+           other.wrapped->SerializeAsString();
+  }
+};
+
+} // namespace caosdb::entity
+
+#endif
diff --git a/proto b/proto
index 41afd36..780c89e 160000
--- a/proto
+++ b/proto
@@ -1 +1 @@
-Subproject commit 41afd36643c0d88aaa104c26c1c6de3c8567e817
+Subproject commit 780c89e86cb9c3b17171b88827aaedee3c3a9595
diff --git a/src/caosdb/entity.cpp b/src/caosdb/entity.cpp
index 127ac01..5999196 100644
--- a/src/caosdb/entity.cpp
+++ b/src/caosdb/entity.cpp
@@ -20,15 +20,19 @@
  *
  */
 #include "caosdb/entity.h"
+#include "caosdb/data_type.h"
 #include "caosdb/entity/v1alpha1/main.pb.h" // for Parent, Arena::CreateMay...
 #include "caosdb/protobuf_helper.h"         // for get_arena
-#include "google/protobuf/arena.h"          // for Arena
+#include "caosdb/value.h"
+#include <google/protobuf/arena.h> // for Arena
 
 namespace caosdb::entity {
 using caosdb::entity::v1alpha1::IdResponse;
 using ProtoParent = caosdb::entity::v1alpha1::Parent;
 using ProtoProperty = caosdb::entity::v1alpha1::Property;
 using ProtoEntity = caosdb::entity::v1alpha1::Entity;
+using ProtoImportance = caosdb::entity::v1alpha1::Importance;
+using caosdb::entity::v1alpha1::EntityRole;
 using caosdb::utility::get_arena;
 
 Parent::Parent() : wrapped(Parent::CreateProtoParent()) {
@@ -90,20 +94,20 @@ auto Property::CreateProtoProperty() -> ProtoProperty * {
   return this->wrapped->description();
 }
 
-[[nodiscard]] auto Property::GetImportance() const -> const std::string & {
-  return this->wrapped->importance();
+[[nodiscard]] auto Property::GetImportance() const -> Importance {
+  return static_cast<Importance>(this->wrapped->importance());
 }
 
-[[nodiscard]] auto Property::GetValue() const -> const std::string & {
-  return this->wrapped->value();
+[[nodiscard]] auto Property::GetValue() const -> const Value & {
+  return this->value;
 }
 
 [[nodiscard]] auto Property::GetUnit() const -> const std::string & {
   return this->wrapped->unit();
 }
 
-[[nodiscard]] auto Property::GetDatatype() const -> const std::string & {
-  return this->wrapped->datatype();
+[[nodiscard]] auto Property::GetDataType() const -> const DataType & {
+  return this->data_type;
 }
 
 auto Property::SetId(const std::string &id) -> void {
@@ -118,20 +122,32 @@ auto Property::SetDescription(const std::string &description) -> void {
   this->wrapped->set_description(description);
 }
 
-auto Property::SetImportance(const std::string &importance) -> void {
-  this->wrapped->set_importance(importance);
+auto Property::SetImportance(Importance importance) -> void {
+  this->wrapped->set_importance(static_cast<ProtoImportance>(importance));
 }
 
-auto Property::SetValue(const std::string &value) -> void {
-  this->wrapped->set_value(value);
+auto Property::SetValue(const Value &value) -> StatusCode {
+  return this->value.CopyFrom(value);
+}
+
+auto Property::SetValue(const std::string &value) -> StatusCode {
+  return SetValue(Value(value));
+}
+
+auto Property::SetValue(double value) -> StatusCode {
+  return SetValue(Value(value));
 }
 
 auto Property::SetUnit(const std::string &unit) -> void {
   this->wrapped->set_unit(unit);
 }
 
-auto Property::SetDatatype(const std::string &datatype) -> void {
-  this->wrapped->set_datatype(datatype);
+auto Property::SetDataType(const DataType &new_data_type) -> StatusCode {
+  return this->data_type.CopyFrom(new_data_type);
+}
+
+auto Property::SetDataType(const std::string &new_data_type) -> StatusCode {
+  return SetDataType(DataType(new_data_type));
 }
 
 auto Properties::Append(const Property &property) -> void {
@@ -190,7 +206,9 @@ auto Entity::CopyTo(ProtoEntity *target) -> void {
   target->CopyFrom(*(this->wrapped));
 }
 
-auto Entity::SetRole(Role role) -> void { this->wrapped->set_role(role); }
+auto Entity::SetRole(Role role) -> void {
+  this->wrapped->set_role(static_cast<EntityRole>(role));
+}
 
 auto Entity::SetName(const std::string &name) -> void {
   this->wrapped->set_name(name);
@@ -200,16 +218,34 @@ auto Entity::SetDescription(const std::string &description) -> void {
   this->wrapped->set_description(description);
 }
 
-auto Entity::SetValue(const std::string &value) -> void {
-  this->wrapped->set_value(value);
+auto Entity::SetValue(const Value &value) -> StatusCode {
+  if (GetRole() != Role::PROPERTY) {
+    return StatusCode::ENTITY_CANNOT_HAVE_A_VALUE;
+  }
+  return this->value.CopyFrom(value);
+}
+
+auto Entity::SetValue(const std::string &value) -> StatusCode {
+  return SetValue(Value(value));
+}
+
+auto Entity::SetValue(double value) -> StatusCode {
+  return SetValue(Value(value));
 }
 
 auto Entity::SetUnit(const std::string &unit) -> void {
   this->wrapped->set_unit(unit);
 }
 
-auto Entity::SetDatatype(const std::string &datatype) -> void {
-  this->wrapped->set_datatype(datatype);
+auto Entity::SetDataType(const DataType &new_data_type) -> StatusCode {
+  if (GetRole() != Role::PROPERTY) {
+    return StatusCode::ENTITY_CANNOT_HAVE_A_DATA_TYPE;
+  }
+  return this->data_type.CopyFrom(new_data_type);
+}
+
+auto Entity::SetDataType(const std::string &new_data_type) -> StatusCode {
+  return SetDataType(DataType(new_data_type));
 }
 
 } // namespace caosdb::entity
diff --git a/test/test_entity.cpp b/test/test_entity.cpp
index bfa0504..7e8ca9d 100644
--- a/test/test_entity.cpp
+++ b/test/test_entity.cpp
@@ -20,16 +20,18 @@
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
  *
  */
+#include "caosdb/data_type.h"                    // for DataType, AtomicDat...
 #include "caosdb/entity.h"                       // for Entity, Parent, Par...
 #include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionSe...
 #include "caosdb/entity/v1alpha1/main.pb.h"      // for IdResponse, Message
 #include "caosdb/message_code.h"                 // for MessageCode
 #include "caosdb/protobuf_helper.h"              // for get_arena
 #include "caosdb/transaction.h"                  // for Transaction
-#include "google/protobuf/arena.h"               // for Arena
-#include "gtest/gtest-message.h"                 // for Message
-#include "gtest/gtest-test-part.h"               // for TestPartResult, Sui...
-#include "gtest/gtest_pred_impl.h"               // for Test, EXPECT_EQ
+#include "caosdb/value.h"                        // for Value
+#include <google/protobuf/arena.h>               // for Arena
+#include <gtest/gtest-message.h>                 // for Message
+#include <gtest/gtest-test-part.h>               // for TestPartResult, Sui...
+#include <gtest/gtest_pred_impl.h>               // for Test, EXPECT_EQ
 #include <memory>                                // for allocator, shared_ptr
 
 namespace caosdb::entity {
@@ -64,17 +66,19 @@ TEST(test_entity, test_property_setters) {
   auto prop = Property();
   prop.SetName("prop_name");
   prop.SetId("prop_id");
-  prop.SetImportance("prop_importance");
+  prop.SetImportance(Importance::OBLIGATORY);
   prop.SetValue("prop_value");
   prop.SetUnit("prop_unit");
-  prop.SetDatatype("prop_dtype");
+  prop.SetDataType("prop_dtype");
 
   EXPECT_EQ(prop.GetName(), "prop_name");
   EXPECT_EQ(prop.GetId(), "prop_id");
-  EXPECT_EQ(prop.GetImportance(), "prop_importance");
-  EXPECT_EQ(prop.GetValue(), "prop_value");
+  EXPECT_EQ(prop.GetImportance(), Importance::OBLIGATORY);
+  EXPECT_TRUE(prop.GetValue().IsString());
+  EXPECT_EQ(prop.GetValue().AsString(), "prop_value");
   EXPECT_EQ(prop.GetUnit(), "prop_unit");
-  EXPECT_EQ(prop.GetDatatype(), "prop_dtype");
+  EXPECT_TRUE(prop.GetDataType().IsReference());
+  EXPECT_EQ(prop.GetDataType().AsReference().GetName(), "prop_dtype");
 }
 
 TEST(test_entity, test_append_property) {
@@ -83,10 +87,10 @@ TEST(test_entity, test_append_property) {
   auto prop = Property();
   prop.SetName("prop_name");
   prop.SetId("prop_id");
-  prop.SetImportance("prop_importance");
+  prop.SetImportance(Importance::RECOMMENDED);
   prop.SetValue("prop_value");
   prop.SetUnit("prop_unit");
-  prop.SetDatatype("prop_dtype");
+  prop.SetDataType("prop_dtype");
 
   EXPECT_EQ(entity.GetProperties().Size(), 0);
   entity.AppendProperty(prop);
@@ -99,12 +103,12 @@ TEST(test_entity, test_append_property) {
   EXPECT_EQ(prop.GetImportance(), same_prop.GetImportance());
   EXPECT_EQ(prop.GetValue(), same_prop.GetValue());
   EXPECT_EQ(prop.GetUnit(), same_prop.GetUnit());
-  EXPECT_EQ(prop.GetDatatype(), same_prop.GetDatatype());
+  EXPECT_EQ(prop.GetDataType(), same_prop.GetDataType());
 }
 
 TEST(test_entity, test_copy_to) {
   auto entity = Entity();
-  entity.SetRole("original_role");
+  entity.SetRole(Role::RECORD);
   entity.SetName("orignial_name");
 
   auto parent = Parent();
@@ -137,15 +141,15 @@ TEST(test_entity, test_insert_entity) {
     std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr));
 
   auto entity = Entity();
-  entity.SetRole("entity_role");
+  entity.SetRole(Role::RECORD_TYPE);
   entity.SetName("entity_name");
 
-  EXPECT_EQ(entity.GetRole(), "entity_role");
+  EXPECT_EQ(entity.GetRole(), Role::RECORD_TYPE);
   EXPECT_EQ(entity.GetName(), "entity_name");
 
   transaction.InsertEntity(&entity);
 
-  EXPECT_EQ(entity.GetRole(), "entity_role");
+  EXPECT_EQ(entity.GetRole(), Role::RECORD_TYPE);
   EXPECT_EQ(entity.GetName(), "entity_name");
 }
 
@@ -154,19 +158,21 @@ TEST(test_entity, test_insert_with_role) {
     std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr));
 
   auto entity = Entity();
-  entity.SetRole("Property");
-  entity.SetDatatype("DOUBLE");
+  entity.SetRole(Role::PROPERTY);
+  entity.SetDataType("DOUBLE");
   entity.SetName("Length");
   entity.SetUnit("m");
-  entity.SetValue("5.5");
+  entity.SetValue(5.5);
 
   transaction.InsertEntity(&entity);
 
-  EXPECT_EQ(entity.GetRole(), "Property");
-  EXPECT_EQ(entity.GetDatatype(), "DOUBLE");
+  EXPECT_EQ(entity.GetRole(), Role::PROPERTY);
+  EXPECT_TRUE(entity.GetDataType().IsAtomic());
+  EXPECT_EQ(entity.GetDataType().AsAtomic(), AtomicDataType::DOUBLE);
   EXPECT_EQ(entity.GetName(), "Length");
   EXPECT_EQ(entity.GetUnit(), "m");
-  EXPECT_EQ(entity.GetValue(), "5.5");
+  EXPECT_TRUE(entity.GetValue().IsDouble());
+  EXPECT_DOUBLE_EQ(entity.GetValue().AsDouble(), 5.5);
 }
 
 TEST(test_entity, test_insert_with_parent) {
@@ -203,10 +209,10 @@ TEST(test_entity, test_insert_with_property) {
   auto prop = Property();
   prop.SetName("prop_name");
   prop.SetId("prop_id");
-  prop.SetImportance("prop_importance");
+  prop.SetImportance(Importance::FIX);
   prop.SetValue("prop_value");
   prop.SetUnit("prop_unit");
-  prop.SetDatatype("prop_dtype");
+  prop.SetDataType("prop_dtype");
 
   entity.AppendProperty(prop);
 
@@ -221,7 +227,7 @@ TEST(test_entity, test_insert_with_property) {
   EXPECT_EQ(prop.GetImportance(), inserted_prop.GetImportance());
   EXPECT_EQ(prop.GetValue(), inserted_prop.GetValue());
   EXPECT_EQ(prop.GetUnit(), inserted_prop.GetUnit());
-  EXPECT_EQ(prop.GetDatatype(), inserted_prop.GetDatatype());
+  EXPECT_EQ(prop.GetDataType(), inserted_prop.GetDataType());
 }
 
 TEST(test_entity, test_from_id_response) {
diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp
index b062cb8..d68ebda 100644
--- a/test/test_transaction.cpp
+++ b/test/test_transaction.cpp
@@ -171,7 +171,7 @@ TEST(test_transaction, test_update_entity) {
   auto transaction = connection.CreateTransaction();
 
   caosdb::entity::Entity update_entity;
-  update_entity.SetRole("New role");
+  update_entity.SetName("New Name");
   auto error = transaction->UpdateEntity(&update_entity);
 
   EXPECT_EQ(error, StatusCode::ORIGINAL_ENTITY_MISSING_ID);
-- 
GitLab