Skip to content
Snippets Groups Projects
Select Git revision
  • a8646421877389d3b32aa45ca4e5364de789af46
  • main default protected
  • f-sss4grpc
  • dev
  • 108-implement-rpc-call-for-server-side-scripting
  • f-windows-conan-create
  • f-to-string
  • f-update-requirements
  • f-related-projects
  • f-role
  • f-remote-path
  • f-rel-path
  • f-consol-message
  • v0.3.0
  • v0.2.2
  • v0.2.1
  • v0.2.0
  • v0.1.2
  • v0.1.1
  • v0.1
  • v0.0.19
  • v0.0.18
  • v0.0.16
  • v0.0.15
  • v0.0.10
  • v0.0.9
  • v0.0.8
  • v0.0.7
  • v0.0.6
  • v0.0.5
  • v0.0.4
  • v0.0.3
  • v0.0.2
33 results

entity.h

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    entity.h 27.78 KiB
    /*
     * This file is a part of the CaosDB Project.
     *
     * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
     * Copyright (C) 2021 Florian Spreckelsen <f.spreckelsen@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/>.
     *
     */
    
    /**
     * @brief Anything entity-related.
     * @file caosdb/entity.h
     * @author Timm Fitchen
     * @date 2021-07-07
     */
    #ifndef CAOSDB_ENTITY_H
    #define CAOSDB_ENTITY_H
    
    #include "caosdb/data_type.h"                          // for DataType
    #include "caosdb/entity/v1alpha1/main.pb.h"            // for RepeatedPtrField
    #include "caosdb/logging.h"                            // for CAOSDB_LOG_WARN
    #include "caosdb/message_code.h"                       // for get_message_code
    #include "caosdb/protobuf_helper.h"                    // for get_arena
    #include "caosdb/status_code.h"                        // for StatusCode
    #include "caosdb/value.h"                              // for Value
    #include <boost/filesystem/operations.hpp>             // for exists, is_di...
    #include <boost/filesystem/path.hpp>                   // for path
    #include <boost/log/core/record.hpp>                   // for record
    #include <boost/log/detail/attachable_sstream_buf.hpp> // for basic_ostring...
    #include <boost/log/sources/record_ostream.hpp>        // for basic_record_...
    #include <boost/preprocessor/seq/limits/enum_256.hpp>  // for BOOST_PP_SEQ_...
    #include <boost/preprocessor/seq/limits/size_256.hpp>  // for BOOST_PP_SEQ_...
    #include <cstdint>                                     // for int64_t
    #include <google/protobuf/message.h>                   // for RepeatedPtrField
    #include <google/protobuf/util/json_util.h>            // for MessageToJson...
    #include <iosfwd>                                      // for streamsize
    #include <iterator>                                    // for iterator, output_iterato...
    #include <map>                                         // for map
    #include <random>                                      // for mt19937, rand...
    #include <stdexcept>                                   // for out_of_range
    #include <string>                                      // for string, basic...
    #include <vector>                                      // for vector
    
    namespace caosdb::entity {
    using boost::filesystem::exists;
    using boost::filesystem::is_directory;
    using caosdb::entity::v1alpha1::IdResponse;
    using ProtoParent = caosdb::entity::v1alpha1::Parent;
    using ProtoProperty = caosdb::entity::v1alpha1::Property;
    using ProtoEntity = caosdb::entity::v1alpha1::Entity;
    using ProtoFileDescriptor = caosdb::entity::v1alpha1::FileDescriptor;
    using ProtoMessage = caosdb::entity::v1alpha1::Message;
    using caosdb::entity::v1alpha1::EntityRole;
    using ProtoImportance = caosdb::entity::v1alpha1::Importance;
    using caosdb::StatusCode;
    using caosdb::entity::v1alpha1::EntityResponse;
    using caosdb::entity::v1alpha1::FileTransmissionId;
    using caosdb::utility::get_arena;
    using ::google::protobuf::RepeatedPtrField;
    using google::protobuf::RepeatedPtrField;
    
    static const std::string logger_name = "caosdb::entity";
    
    /**
     * The property importance.
     */
    enum class 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.
    };
    const std::map<Importance, std::string> importance_names = {
      {Importance::UNSPECIFIED, "UNSPECIFIED"},
      {Importance::OBLIGATORY, "OBLIGATORY"},
      {Importance::RECOMMENDED, "RECOMMENDED"},
      {Importance::SUGGESTED, "SUGGESTED"},
      {Importance::FIX, "FIX"}};
    
    /**
     * The entity role.
     */
    enum class 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
    };
    const std::map<Role, std::string> role_names = {{Role::UNSPECIFIED, "UNSPECIFIED"},
                                                    {Role::RECORD_TYPE, "RECORD_TYPE"},
                                                    {Role::RECORD, "RECORD"},
                                                    {Role::PROPERTY, "PROPERTY"},
                                                    {Role::FILE, "FILE"}};
    
    struct FileDescriptor {
      FileTransmissionId *file_transmission_id;
      ProtoFileDescriptor *wrapped;
      boost::filesystem::path local_path;
    };
    
    /**
     * Abstract base class for Messages, Properties and Parents container classes.
     *
     * This is a list-like class.
     */
    template <typename T, typename P> class RepeatedPtrFieldWrapper {
      class iterator;
    
    public:
      /**
       * Return the current size of the container.
       */
      [[nodiscard]] inline auto size() const -> int { return wrapped->size(); }
      /**
       * Return a const reference to the element at the given index.
       */
      [[nodiscard]] inline auto at(int index) const -> const T & { return *mutable_at(index); }
    
      /**
       * Return a mutable pointer to the element at the given index.
       */
      [[nodiscard]] inline auto mutable_at(int index) const -> T * {
        if (index >= size() || index < 0) {
          throw std::out_of_range("Container has size " + std::to_string(size()));
        }
        if (cache.count(index) == 0) {
          cache.emplace(index, T(&(wrapped->at(index))));
        }
        return &(cache.at(index));
      }
      /**
       * Return iterator positioned at the beginning of the list.
       */
      auto begin() -> iterator;
      /**
       * Return iterator positioned at the end of the list.
       */
      auto end() -> iterator;
      /**
       * Return constant iterator positioned at the beginning of the list.
       */
      auto begin() const -> const iterator;
      /**
       * Return constant iterator positioned at the end of the list.
       */
      auto end() const -> const iterator;
    
      friend class Entity;
    
      virtual ~RepeatedPtrFieldWrapper(){};
    
    protected:
      RepeatedPtrFieldWrapper(){};
      explicit inline RepeatedPtrFieldWrapper(::google::protobuf::RepeatedPtrField<P> *wrapped)
        : wrapped(wrapped){};
    
      /**
       * Append an element. This adds the element to the end of the wrapped list
       * and increases the size by one.
       */
      inline auto Append(const T &element) -> void {
        auto *destination = this->wrapped->Add();
        destination->Swap(element.wrapped);
    
        // Clear the originally wrapped object and return it to the Arena
        element.wrapped->Clear();
    
        // set the pointer to the new object which is owned by the RepeatedPtrField
        element.wrapped = destination;
      }
    
      /**
       * Remove the element at the given index.
       */
      inline auto Remove(int index) -> void {
        this->wrapped->DeleteSubrange(index, 1);
        if (cache.count(index) > 0) {
          cache.erase(index);
        }
    
        // shift all indices in the cache above index (such that the values do not
        // get deleted/copied because this could destroy pointers (c-interface).
        for (int i = index + 1; i < size(); i++) {
          if (cache.count(i) > 0) {
            auto handle = cache.extract(i);
            handle.key()--;
            cache.insert(std::move(handle));
          }
        }
      }
    
      inline auto Clear() noexcept -> void { this->wrapped->Clear(); }
    
      ::google::protobuf::RepeatedPtrField<P> *wrapped;
      mutable std::map<int, T> cache;
    
    private:
      class iterator : public std::iterator<std::output_iterator_tag, T> {
      public:
        explicit iterator(const RepeatedPtrFieldWrapper<T, P> *instance, int index = 0);
        // TODO(henrik) add unit tests
        auto operator*() const -> T &;
        auto operator++() -> iterator &;
        auto operator++(int) -> iterator;
        auto operator!=(const iterator &rhs) const -> bool;
    
      private:
        int current_index = 0;
        const RepeatedPtrFieldWrapper<T, P> *instance;
      };
    };
    
    template <class T, class P>
    RepeatedPtrFieldWrapper<T, P>::iterator::iterator(const RepeatedPtrFieldWrapper<T, P> *instance,
                                                      int index)
      : current_index(index), instance(instance) {}
    
    template <typename T, typename P>
    auto RepeatedPtrFieldWrapper<T, P>::iterator::operator*() const -> T & {
      return *(this->instance->mutable_at(current_index));
    }
    
    template <typename T, typename P>
    auto RepeatedPtrFieldWrapper<T, P>::iterator::operator++()
      -> RepeatedPtrFieldWrapper<T, P>::iterator & {
      current_index++;
      return *this;
    }
    
    template <typename T, typename P>
    auto RepeatedPtrFieldWrapper<T, P>::iterator::operator++(int)
      -> RepeatedPtrFieldWrapper<T, P>::iterator {
      iterator tmp(*this);
      operator++();
      return tmp;
    }
    
    template <typename T, typename P>
    auto RepeatedPtrFieldWrapper<T, P>::iterator::operator!=(const iterator &rhs) const -> bool {
      return this->current_index != rhs.current_index;
    }
    
    template <typename T, typename P>
    auto RepeatedPtrFieldWrapper<T, P>::begin() -> RepeatedPtrFieldWrapper<T, P>::iterator {
      return RepeatedPtrFieldWrapper<T, P>::iterator(this, 0);
    }
    
    template <typename T, typename P>
    auto RepeatedPtrFieldWrapper<T, P>::end() -> RepeatedPtrFieldWrapper<T, P>::iterator {
      return RepeatedPtrFieldWrapper<T, P>::iterator(this, size());
    }
    
    template <typename T, typename P>
    auto RepeatedPtrFieldWrapper<T, P>::begin() const -> const RepeatedPtrFieldWrapper<T, P>::iterator {
      return RepeatedPtrFieldWrapper<T, P>::iterator(this, 0);
    }
    
    template <typename T, typename P>
    auto RepeatedPtrFieldWrapper<T, P>::end() const -> const RepeatedPtrFieldWrapper<T, P>::iterator {
      return RepeatedPtrFieldWrapper<T, P>::iterator(this, size());
    }
    
    /**
     * Messages convey information about the state of entities and result of
     * transactions.
     *
     * A Message object can be thought of as kinf of a generalized error object in
     * other frameworks. Please have a look at MessageCodes for more details.
     */
    class Message {
    public:
      /**
       * Get the code of this message.
       *
       * The message code is a unique identifier of the type of message and is
       * intended to make it easiert to identify messages in a machine-readable
       * way.
       */
      [[nodiscard]] inline auto GetCode() const -> MessageCode {
        return get_message_code(wrapped->code());
      }
      /**
       * Get the description of this message.
       *
       * The description is intended for a human reader.
       */
      [[nodiscard]] inline auto GetDescription() const -> std::string { return wrapped->description(); }
    
      friend class Entity;
      // TODO(fspreck) Re-enable once we have decided how messages are
      // appended to parents and properties
      // friend class Parent;
      // friend class Property;
      friend class Messages;
      friend class RepeatedPtrFieldWrapper<Message, ProtoMessage>;
    
    private:
      explicit inline Message(ProtoMessage *wrapped) : wrapped(wrapped){};
    
      ProtoMessage *wrapped;
    };
    
    /**
     * Container for Messages.
     */
    class Messages : public RepeatedPtrFieldWrapper<Message, ProtoMessage> {
    public:
      ~Messages();
    
      friend class Entity;
      // TODO(fspreck) Same here.
      // friend class Parent;
      // friend class Property;
    
    private:
      inline Messages() : RepeatedPtrFieldWrapper(){};
    };
    
    /**
     * Parent of an Entity.
     *
     * This implementation uses protobuf messages as storage backends. In other
     * words, this class wraps a protobuf message and provides getter and setter
     * methods.
     */
    class Parent {
    public:
      explicit inline Parent(ProtoParent *wrapped) : wrapped(wrapped){};
      Parent();
    
      /**
       * Return the id of the parent entity.
       */
      [[nodiscard]] auto GetId() const -> const std::string &;
      /**
       * Return the name of the parent entity.
       */
      [[nodiscard]] auto GetName() const -> const std::string &;
      /**
       * Return the description of the parent entity.
       */
      [[nodiscard]] auto GetDescription() const -> const std::string &;
    
      /**
       * Set the id of the parent.
       */
      auto SetId(const std::string &id) -> void;
      /**
       * Set the name of the parent.
       */
      auto SetName(const std::string &name) -> void;
    
      /**
       * Return a json string representing this parent.
       *
       * This is intended for debugging.
       */
      inline auto ToString() const -> const std::string {
        google::protobuf::util::JsonPrintOptions options;
        options.add_whitespace = true;
        std::string out;
        google::protobuf::util::MessageToJsonString(*(this->wrapped), &out, options);
        return out;
      }
    
      // TODO(fspreck) Finish the following implementations once we have
      // decided how to attach messages to parents.
      // /**
      //  * Return the error messages of this parent.
      //  */
      // [[nodiscard]] inline auto GetErrors() const -> const Messages & {
      //   return errors;
      // }
      // [[nodiscard]] inline auto HasErrors() const -> bool {
      //   return this->errors.wrapped->size() > 0;
      // }
      // /**
      //  * Return the warning messages of this parent.
      //  */
      // [[nodiscard]] inline auto GetWarnings() const -> const Messages & {
      //   return warnings;
      // }
      // [[nodiscard]] inline auto HasWarnings() const -> bool {
      //   return this->warnings.wrapped->size() > 0;
      // }
      // /**
      //  * Return the info messages of this parent.
      //  */
      // [[nodiscard]] inline auto GetInfos() const -> const Messages & {
      //   return infos;
      // }
    
      friend class Entity;
      friend class Parents;
      friend class RepeatedPtrFieldWrapper<Parent, ProtoParent>;
    
    private:
      /**
       * Return an empty protobuf message pointer.
       *
       * This function is called by the default constructor of the
       * caosdb::entity::Parent class and the protobuf message is used as the
       * storage-backend for the new Parent instance.
       *
       * An 'Arena' takes care of the the memory management. Don't try to delete
       * this.
       */
      static auto CreateProtoParent() -> ProtoParent *;
    
      /**
       * Message which serves as storage backend.
       */
      mutable ProtoParent *wrapped;
      // Messages errors;
      // Messages warnings;
      // Messages infos;
    };
    
    /**
     * Container for parents of entities.
     *
     * Should only be instantiated and write-accessed by the owning entity.
     */
    class Parents : public RepeatedPtrFieldWrapper<Parent, ProtoParent> {
    public:
      ~Parents() = default;
      friend class Entity;
    
    private:
      inline Parents() : RepeatedPtrFieldWrapper(){};
      explicit inline Parents(
        ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Parent> *wrapped)
        : RepeatedPtrFieldWrapper(wrapped){};
    };
    
    /**
     * Property of an Entity.
     *
     * This is a property which belongs to another entity. Don't confuse it with
     * an Entity with the "Property" role.
     */
    class Property {
    public:
      explicit inline Property(ProtoProperty *other)
        : value(Value(other->mutable_value())), data_type(DataType(other->mutable_data_type())),
          wrapped(other){};
      Property();
    
      /**
       * Return the id of this  property
       */
      [[nodiscard]] auto GetId() const -> const std::string &;
      /**
       * Return the name of this  property
       */
      [[nodiscard]] auto GetName() const -> const std::string &;
      /**
       * Return the description of this  property
       */
      [[nodiscard]] auto GetDescription() const -> const std::string &;
      /**
       * Return the importance of this  property
       */
      [[nodiscard]] auto GetImportance() const -> Importance;
      /**
       * Return the value of this  property
       */
      [[nodiscard]] auto GetValue() const -> const Value &;
      /**
       * Return the unit of this  property
       */
      [[nodiscard]] auto GetUnit() const -> const std::string &;
      /**
       * Return the datatype of this  property
       */
      [[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 &;
      // [[nodiscard]] auto GetWarnings() const -> const Messages &;
      // [[nodiscard]] auto GetInfos() const -> const Messages &;
    
      /**
       * Set the id of this property.
       */
      auto SetId(const std::string &id) -> void;
      /**
       * Set the name of this property.
       */
      auto SetName(const std::string &name) -> void;
      /**
       * Set the description of this property.
       */
      auto SetDescription(const std::string &description) -> void;
      /**
       * Set the importance of this property.
       */
      auto SetImportance(Importance importance) -> void;
      /**
       * Set the value of this property.
       */
      auto SetValue(const Value &value) -> StatusCode;
      // auto SetValue(const AbstractValue &value) -> StatusCode;
      auto SetValue(const std::string &value) -> StatusCode;
      auto SetValue(const char *value) -> StatusCode;
      auto SetValue(const double value) -> StatusCode;
      auto SetValue(const std::vector<std::string> &values) -> StatusCode;
      auto SetValue(const std::vector<char *> &values) -> StatusCode;
      auto SetValue(const std::vector<int64_t> &values) -> StatusCode;
      auto SetValue(const std::vector<int> &values) -> StatusCode;
      auto SetValue(const std::vector<double> &values) -> StatusCode;
      auto SetValue(const std::vector<bool> &values) -> StatusCode;
      auto SetValue(const int64_t value) -> StatusCode;
      auto SetValue(const int value) -> StatusCode;
      auto SetValue(const bool value) -> StatusCode;
    
      /**
       * Set the unit of this property.
       */
      auto SetUnit(const std::string &unit) -> void;
      /**
       * Set the datatype of this property.
       */
      auto SetDataType(const DataType &new_data_type) -> StatusCode;
      auto SetDataType(const AtomicDataType new_data_type, bool list_type = false) -> StatusCode;
      auto SetDataType(const std::string &new_data_type, bool list_type = false) -> StatusCode;
    
      /**
       * Return a json string representing this property.
       *
       * This is intended for debugging
       */
      inline auto ToString() const -> const std::string {
        google::protobuf::util::JsonPrintOptions options;
        options.add_whitespace = true;
        std::string out;
        google::protobuf::util::MessageToJsonString(*(this->wrapped), &out, options);
        return out;
      }
    
      friend class Entity;
      friend class Properties;
      friend class RepeatedPtrFieldWrapper<Property, ProtoProperty>;
    
    private:
      static auto CreateProtoProperty() -> ProtoProperty *;
      Value value;
      DataType data_type;
    
      mutable ProtoProperty *wrapped;
    };
    
    /**
     * Container for Properties of Entities.
     *
     * Should only be instantiated and write-accessed by the owning entity.
     *
     * Note that iterating over the Property contents only works via references,
     * since the Property copy constructor is deliberately disabled:
     *
     * \code
     * // Accessing single properties as reference
     * auto &property = my_properties.at(0);
     * // Iterating via reference
     * for (auto &property : my_properties) {...}
     * \endcode
     */
    class Properties : public RepeatedPtrFieldWrapper<Property, caosdb::entity::v1alpha1::Property> {
    public:
      ~Properties() = default;
      friend class Entity;
    
    private:
      inline Properties(){};
      explicit inline Properties(
        ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Property> *wrapped)
        : RepeatedPtrFieldWrapper<Property, caosdb::entity::v1alpha1::Property>(wrapped){};
    };
    
    /**
     * Entity is the central and basic data object of CaosDB.
     *
     * This class is a wrapper of the Entity class auto-generated by protobuf
     * (henceforth "ProtoEntity").
     *
     * Overview of the Constructors:
     *
     * <li> Entity() - Calls Entity(ProtoEntity *) with a fresh ProtoEntity
     * <li> Entity(Entity) - Copy constructor, calls Entity(ProtoEntity *) after copying wrapped
     * ProtoEntity of the original, then also copies all Messages. <li> Entity(ProtoEntity *) - The
     * workhorse of the constructors. Initializes everything and does not call other Entity
     * constructors. <li> Entity(EntityResponse *) - Constructor which is used by the Transaction class
     * to create an Entity from the server's response, calls Entity(ProtoEntity). <li> Entity(IdResponse
     * *) - Constructor which is used by the Transaction class to create an Entity from the servers's
     * response. calls Entity(), then moves the data to the wrapped ProtoEntity.
     *
     */
    class Entity {
    public:
      inline Entity(const Entity &original) : Entity(Copy(*original.wrapped)) {
        this->errors.wrapped->CopyFrom(*original.errors.wrapped);
        this->warnings.wrapped->CopyFrom(*original.warnings.wrapped);
        this->infos.wrapped->CopyFrom(*original.infos.wrapped);
      };
      explicit Entity(ProtoEntity *other)
        : wrapped(other), value(Value(other->mutable_value())),
          data_type(DataType(other->mutable_data_type())) {
        data_type.wrapped = this->wrapped->mutable_data_type();
        value.wrapped = this->wrapped->mutable_value();
        properties.wrapped = this->wrapped->mutable_properties();
        parents.wrapped = this->wrapped->mutable_parents();
        errors.wrapped = CreateMessagesField();
        warnings.wrapped = CreateMessagesField();
        infos.wrapped = CreateMessagesField();
      };
      explicit inline Entity(EntityResponse *response) : Entity(response->release_entity()) {
        this->errors.wrapped->Swap(response->mutable_errors());
        this->warnings.wrapped->Swap(response->mutable_warnings());
        this->infos.wrapped->Swap(response->mutable_infos());
      };
    
      explicit inline Entity(IdResponse *id_response) : Entity() {
        this->wrapped->set_id(id_response->id());
        this->wrapped->mutable_version()->Swap(id_response->mutable_version());
        this->errors.wrapped->Swap(id_response->mutable_errors());
        this->warnings.wrapped->Swap(id_response->mutable_warnings());
        this->infos.wrapped->Swap(id_response->mutable_infos());
      };
    
      explicit inline Entity() : Entity(Entity::CreateProtoEntity()){};
    
      [[nodiscard]] inline auto GetId() const noexcept -> const std::string & { return wrapped->id(); };
      [[nodiscard]] inline auto HasId() const noexcept -> bool { return !wrapped->id().empty(); }
      [[nodiscard]] inline auto GetVersionId() const -> const std::string & {
        return wrapped->version().id();
      };
    
      [[nodiscard]] inline auto GetRole() const -> Role { return static_cast<Role>(wrapped->role()); };
      [[nodiscard]] inline auto GetName() const -> const std::string & { return wrapped->name(); };
      [[nodiscard]] inline auto GetDescription() const -> const std::string & {
        return wrapped->description();
      };
    
      [[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 Value & { return this->value; };
    
      [[nodiscard]] auto GetParents() const -> const Parents &;
      // TODO(henrik) const prevents properties from being changed
      // what about an interface that operates on the list directly?
      [[nodiscard]] auto GetProperties() const -> const Properties &;
      [[nodiscard]] inline auto GetErrors() const -> const Messages & { return errors; }
      [[nodiscard]] inline auto HasErrors() const -> bool { return this->errors.wrapped->size() > 0; }
      [[nodiscard]] auto GetWarnings() const -> const Messages & { return warnings; }
      [[nodiscard]] inline auto HasWarnings() const -> bool {
        return this->warnings.wrapped->size() > 0;
      }
      [[nodiscard]] auto GetInfos() const -> const Messages & { return infos; }
      [[nodiscard]] inline auto HasInfos() const -> bool { return this->infos.wrapped->size() > 0; }
    
      inline auto ToString() const -> const std::string {
        google::protobuf::util::JsonPrintOptions options;
        options.add_whitespace = true;
        std::string out;
        google::protobuf::util::MessageToJsonString(*(this->wrapped), &out, options);
        return out;
      }
    
      auto SetRole(Role role) -> void;
      auto SetName(const std::string &name) -> void;
      /**
       * Set the description of this entity.
       */
      auto SetDescription(const std::string &description) -> void;
    
      // auto SetValue(const AbstractValue &value) -> StatusCode;
      auto SetValue(const Value &value) -> StatusCode;
      auto SetValue(const std::string &value) -> StatusCode;
      auto SetValue(const char *value) -> StatusCode;
      auto SetValue(const double value) -> StatusCode;
      auto SetValue(const std::vector<std::string> &values) -> StatusCode;
      auto SetValue(const std::vector<char *> &values) -> StatusCode;
      auto SetValue(const std::vector<int64_t> &values) -> StatusCode;
      auto SetValue(const std::vector<int> &values) -> StatusCode;
      auto SetValue(const std::vector<double> &values) -> StatusCode;
      auto SetValue(const std::vector<bool> &values) -> StatusCode;
      auto SetValue(const int64_t value) -> StatusCode;
      auto SetValue(const int value) -> StatusCode;
      auto SetValue(const bool value) -> StatusCode;
    
      auto SetUnit(const std::string &unit) -> void;
    
      auto SetDataType(const DataType &new_data_type) -> StatusCode;
      auto SetDataType(const AtomicDataType new_data_type, bool list_type = false) -> StatusCode;
      auto SetDataType(const std::string &new_data_type, bool list_type = false) -> StatusCode;
    
      auto AppendProperty(const Property &property) -> void;
      auto RemoveProperty(int index) -> void;
    
      auto AppendParent(const Parent &parent) -> void;
      auto RemoveParent(int index) -> void;
      /**
       * Copy all of this entity's features to the target ProtoEntity.
       */
      auto CopyTo(ProtoEntity *target) -> void;
    
      auto SetFilePath(const std::string &path) -> void;
      inline auto HasFile() const -> bool { return !this->file_descriptor.local_path.empty(); }
      auto SetFileTransmissionRegistrationId(const std::string &registration_id) -> void;
      inline auto SetFileTransmissionId(FileTransmissionId *file_transmission_id) -> void {
        file_transmission_id->set_file_id(GetNextFileId());
        file_descriptor.file_transmission_id = file_transmission_id;
      }
    
      inline auto GetFileDescriptor() -> FileDescriptor & { return this->file_descriptor; }
    
      inline auto GetLocalPath() const noexcept -> const boost::filesystem::path & {
        return this->file_descriptor.local_path;
      }
    
      inline auto SetLocalPath(const boost::filesystem::path &local_path) noexcept -> StatusCode {
        if (GetRole() != Role::FILE) {
          CAOSDB_LOG_WARN(logger_name) << "Entity::SetLocalPath failed. This is not a file entity.";
          return StatusCode::NOT_A_FILE_ENTITY;
        }
        if (!exists(local_path)) {
          CAOSDB_LOG_WARN(logger_name)
            << "Entity::SetLocalPath failed. This file does not exists: " << local_path.string();
          return StatusCode::FILE_DOES_NOT_EXIST_LOCALLY;
        }
        if (is_directory(local_path)) {
          CAOSDB_LOG_WARN(logger_name)
            << "Entity::SetLocalPath failed. This file is a directory: " << local_path.string();
          return StatusCode::PATH_IS_A_DIRECTORY;
        }
    
        CAOSDB_LOG_TRACE(logger_name) << "Entity::SetLocalPath(" << local_path.string() << ");";
        this->file_descriptor.local_path = local_path;
        return StatusCode::SUCCESS;
      }
    
      inline auto ClearMessages() noexcept -> void {
        errors.Clear();
        warnings.Clear();
        infos.Clear();
      }
    
    private:
      static inline auto Copy(const ProtoEntity &from) -> ProtoEntity * {
        auto to = from.New();
        to->CopyFrom(from);
        return to;
      }
      inline auto GetNextFileId() -> std::string {
        std::string str = "0123456789abcdef";
        std::mt19937 generator(std::random_device{}());
        std::uniform_int_distribution<int> distribution(0, str.size() - 1);
        std::string result(10, '\0');
    
        for (auto &dis : result) {
          dis = str[distribution(generator)];
        }
        return result;
      }
      static auto CreateProtoEntity() -> ProtoEntity *;
      static auto CreateMessagesField() -> RepeatedPtrField<ProtoMessage> *;
      auto SetId(const std::string &id) -> void;
      auto SetVersionId(const std::string &id) -> void;
    
    private:
      FileDescriptor file_descriptor;
      ProtoEntity *wrapped;
      Properties properties;
      Parents parents;
      Messages errors;
      Messages warnings;
      Messages infos;
      Value value;
      DataType data_type;
    };
    
    } // namespace caosdb::entity
    #endif