diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h index e731f9a0d31f8a8e792ad614ea86e16d260b1b0d..99f38591fe6426c6a6b6ca1aad26d6bce5fa1aa4 100644 --- a/include/caosdb/entity.h +++ b/include/caosdb/entity.h @@ -2,6 +2,7 @@ * 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 @@ -36,6 +37,7 @@ 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; /** @@ -54,8 +56,10 @@ public: } friend class Entity; - friend class Parent; - friend class Property; + // 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; private: @@ -76,8 +80,9 @@ public: } friend class Entity; - friend class Parent; - friend class Property; + // TODO(fspreck) Same here. + // friend class Parent; + // friend class Property; private: inline Messages() : wrapped(nullptr){}; @@ -134,32 +139,32 @@ public: return out; } - // TODO(fspreck) These need implementations. See Entity::GetErrors for - // inspiration. - /** - * 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; - } + // 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; @@ -181,9 +186,9 @@ private: * Message which serves as storage backend. */ mutable caosdb::entity::v1alpha1::Parent *wrapped; - Messages errors; - Messages warnings; - Messages infos; + // Messages errors; + // Messages warnings; + // Messages infos; }; /** @@ -239,31 +244,88 @@ class Property { public: explicit inline Property(caosdb::entity::v1alpha1::Property *wrapped) : wrapped(wrapped){}; + Property(); - // TODO(fspreck) All of these methods need implementations. + /** + * 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 -> const std::string &; + /** + * Return the value of this property + */ [[nodiscard]] auto GetValue() const -> const std::string &; + /** + * Return the unit of this property + */ [[nodiscard]] auto GetUnit() const -> const std::string &; + /** + * Return the datatype of this property + */ [[nodiscard]] auto GetDatatype() const -> const std::string &; - [[nodiscard]] auto GetErrors() const -> const Messages &; - [[nodiscard]] auto GetWarnings() const -> const Messages &; - [[nodiscard]] auto GetInfos() const -> const Messages &; + // 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 importance of this property. + */ auto SetImportance(const std::string &importance) -> void; + /** + * Set the value of this property. + */ auto SetValue(const std::string &value) -> void; + /** + * Set the unit of this property. + */ auto SetUnit(const std::string &unit) -> void; + /** + * Set the datatype of this property. + */ auto SetDatatype(const std::string &datatype) -> void; + /** + * Return a json string representing this property. + * + * This is intended for debugging + */ + inline auto ToString() const -> const std::string { + google::protobuf::util::JsonOptions options; + std::string out; + google::protobuf::util::MessageToJsonString(*(this->wrapped), &out, + options); + + return out; + } + friend class Entity; friend class Properties; private: - caosdb::entity::v1alpha1::Property *wrapped; + static auto CreateProtoProperty() -> ProtoProperty *; + + mutable caosdb::entity::v1alpha1::Property *wrapped; }; /** @@ -273,10 +335,18 @@ private: */ class Properties { public: - // TODO(fspreck) Implementations needed (basically everything). See Parents - // container for inspiration. - [[nodiscard]] auto At(int index) const -> const Property &; - auto Append(const Property &property) -> void; + /** + * Return the current size of the properties container. + * + * This is also the number of properties the owningn entity currently has. + */ + [[nodiscard]] inline auto Size() const -> int { return wrapped->size(); } + /** + * Return the property at the given index. + */ + [[nodiscard]] auto At(int index) const -> const Property { + return Property(&(wrapped->at(index))); + } friend class Entity; @@ -287,6 +357,13 @@ private: *wrapped) : wrapped(wrapped){}; + /** + * Append a property + * + * This increases the Size() by one. + */ + auto Append(const Property &property) -> void; + ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Property> *wrapped; }; @@ -366,11 +443,14 @@ public: auto SetUnit(const std::string &unit) -> void; // Currently no references or lists. auto SetDatatype(const std::string &datatype) -> void; - // TODO(fspreck) this one is tricky. See AppendParent auto AppendProperty(const Property &property) -> void; auto AppendParent(const Parent &parent) -> void; auto Switch(ProtoEntity *entity) -> void; + /** + * Copy all of this entity's features to the target ProtoEntity. + */ + auto CopyTo(ProtoEntity *target) -> void; private: static auto CreateProtoEntity() -> ProtoEntity *; diff --git a/src/caosdb/entity.cpp b/src/caosdb/entity.cpp index 84105eda6bebbead819d66ae92f5b693fc293481..999ba3753b068909dc7dd54518ca5ff8bd8033bf 100644 --- a/src/caosdb/entity.cpp +++ b/src/caosdb/entity.cpp @@ -1,6 +1,8 @@ /* * 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 @@ -25,13 +27,16 @@ 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 caosdb::utility::get_arena; Parent::Parent() : wrapped(Parent::CreateProtoParent()) { - errors.wrapped = this->wrapped->mutable_errors(); - warnings.wrapped = this->wrapped->mutable_warnings(); - infos.wrapped = this->wrapped->mutable_infos(); + // TODO(fspreck) Re-enable once we have decided how to attach + // messages to parents. + // errors.wrapped = this->wrapped->mutable_errors(); + // warnings.wrapped = this->wrapped->mutable_warnings(); + // infos.wrapped = this->wrapped->mutable_infos(); } auto Parent::CreateProtoParent() -> ProtoParent * { @@ -63,6 +68,73 @@ auto Parents::Append(const Parent &parent) -> void { parent.wrapped = destination; } +Property::Property() : wrapped(Property::CreateProtoProperty()) {} + +auto Property::CreateProtoProperty() -> ProtoProperty * { + return google::protobuf::Arena::CreateMessage<ProtoProperty>(get_arena()); +} + +[[nodiscard]] auto Property::GetId() const -> const std::string & { + return this->wrapped->id(); +} + +[[nodiscard]] auto Property::GetName() const -> const std::string & { + return this->wrapped->name(); +} + +[[nodiscard]] auto Property::GetDescription() const -> const std::string & { + return this->wrapped->description(); +} + +[[nodiscard]] auto Property::GetImportance() const -> const std::string & { + return this->wrapped->importance(); +} + +[[nodiscard]] auto Property::GetValue() const -> const std::string & { + return this->wrapped->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(); +} + +auto Property::SetId(const std::string &id) -> void { + this->wrapped->set_id(id); +} + +auto Property::SetName(const std::string &name) -> void { + this->wrapped->set_name(name); +} + +auto Property::SetImportance(const std::string &importance) -> void { + this->wrapped->set_importance(importance); +} + +auto Property::SetValue(const std::string &value) -> void { + this->wrapped->set_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 Properties::Append(const Property &property) -> void { + auto *destination = this->wrapped->Add(); + destination->Swap(property.wrapped); + + property.wrapped->Clear(); + + property.wrapped = destination; +} + [[nodiscard]] auto Entity::GetParents() const -> const Parents & { return parents; } @@ -71,6 +143,14 @@ auto Entity::AppendParent(const Parent &parent) -> void { this->parents.Append(parent); } +[[nodiscard]] auto Entity::GetProperties() const -> const Properties & { + return properties; +} + +auto Entity::AppendProperty(const Property &property) -> void { + this->properties.Append(property); +} + auto Entity::CreateProtoEntity() -> ProtoEntity * { return google::protobuf::Arena::CreateMessage<ProtoEntity>(get_arena()); } @@ -97,12 +177,17 @@ auto Entity::SetVersionId(const std::string &id) -> void { this->wrapped->mutable_version()->set_id(id); } +// TODO(tf) Re-implement s.th. properties and parents are kept. auto Entity::Switch(ProtoEntity *entity) -> void { this->wrapped->Swap(entity); this->wrapped->Clear(); this->wrapped = entity; } +auto Entity::CopyTo(ProtoEntity *target) -> void { + target->CopyFrom(*(this->wrapped)); +} + auto Entity::SetRole(const std::string &role) -> void { this->wrapped->set_role(role); } diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp index 72b59e81cf51f86d2f7cb8fe3f482753af590da5..b9482232c63e55c6fb545d4b9e2ffa35ae7c13aa 100644 --- a/src/caosdb/transaction.cpp +++ b/src/caosdb/transaction.cpp @@ -137,8 +137,8 @@ auto Transaction::InsertEntity(Entity *entity) noexcept -> StatusCode { auto *sub_request = this->request->add_requests(); auto *proto_entity = sub_request->mutable_insert_request(); - entity->Switch(proto_entity); - + // copy the original entity for the transaction + entity->CopyTo(proto_entity); return StatusCode::INITIAL; } diff --git a/test/test_entity.cpp b/test/test_entity.cpp index 66423eb49300422439a75ec20f35e63d703053c1..ec117968e3a8d72304eacd27663889cf1cd156f0 100644 --- a/test/test_entity.cpp +++ b/test/test_entity.cpp @@ -3,6 +3,7 @@ * 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 @@ -23,7 +24,9 @@ #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 @@ -33,6 +36,8 @@ namespace caosdb::entity { using caosdb::entity::v1alpha1::IdResponse; +using ProtoEntity = caosdb::entity::v1alpha1::Entity; +using caosdb::utility::get_arena; TEST(test_entity, test_parent_setters) { auto parent = Parent(); @@ -56,6 +61,79 @@ TEST(test_entity, test_append_parent) { EXPECT_EQ(same_parent.GetId(), "some-id"); } +TEST(test_entity, test_property_setters) { + auto prop = Property(); + prop.SetName("prop_name"); + prop.SetId("prop_id"); + prop.SetImportance("prop_importance"); + prop.SetValue("prop_value"); + prop.SetUnit("prop_unit"); + 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.GetUnit(), "prop_unit"); + EXPECT_EQ(prop.GetDatatype(), "prop_dtype"); +} + +// TODO(fspreck) cognitive complexity > 25 (threshold) +TEST(test_entity, test_append_property) { // NOLINT + auto entity = Entity(); + + auto prop = Property(); + prop.SetName("prop_name"); + prop.SetId("prop_id"); + prop.SetImportance("prop_importance"); + prop.SetValue("prop_value"); + prop.SetUnit("prop_unit"); + prop.SetDatatype("prop_dtype"); + + EXPECT_EQ(entity.GetProperties().Size(), 0); + entity.AppendProperty(prop); + EXPECT_EQ(entity.GetProperties().Size(), 1); + + auto same_prop = entity.GetProperties().At(0); + + EXPECT_EQ(prop.GetName(), same_prop.GetName()); + EXPECT_EQ(prop.GetId(), same_prop.GetId()); + 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()); +} + +TEST(test_entity, test_copy_to) { + auto entity = Entity(); + entity.SetId("original_id"); + entity.SetName("orignial_name"); + + auto parent = Parent(); + parent.SetId("parent_id"); + parent.SetName("parent_name"); + entity.AppendParent(parent); + + auto prop = Property(); + prop.SetId("prop_id"); + prop.SetName("prop_name"); + entity.AppendProperty(prop); + + // create protobuf entity to which all fields ae copied and then a + // CaoosDB entity from that protobuf entity. + auto *proto_copy = + google::protobuf::Arena::CreateMessage<ProtoEntity>(get_arena()); + entity.CopyTo(proto_copy); + auto copied = Entity(proto_copy); + + EXPECT_EQ(entity.GetId(), copied.GetId()); + EXPECT_EQ(entity.GetName(), copied.GetName()); + EXPECT_EQ(copied.GetParents().At(0).GetId(), parent.GetId()); + EXPECT_EQ(copied.GetParents().At(0).GetName(), parent.GetName()); + EXPECT_EQ(copied.GetProperties().At(0).GetId(), prop.GetId()); + EXPECT_EQ(copied.GetProperties().At(0).GetName(), prop.GetName()); +} + TEST(test_entity, test_insert_entity) { auto transaction = caosdb::transaction::Transaction( std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr)); @@ -98,6 +176,63 @@ TEST(test_entity, test_insert_with_role) { // NOLINT EXPECT_EQ(entity.GetValue(), "5.5"); } +TEST(test_entity, test_insert_with_parent) { + auto transaction = caosdb::transaction::Transaction( + std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr)); + + auto entity = Entity(); + entity.SetId("entity_id"); + + auto parent = Parent(); + parent.SetId("parent_id"); + parent.SetName("parent_name"); + EXPECT_EQ(parent.GetId(), "parent_id"); + EXPECT_EQ(parent.GetName(), "parent_name"); + + entity.AppendParent(parent); + + transaction.InsertEntity(&entity); + + std::cout << entity.ToString() << std::endl; + EXPECT_EQ(entity.GetId(), "entity_id"); + EXPECT_EQ(entity.GetParents().Size(), 1); + auto inserted_parent = entity.GetParents().At(0); + EXPECT_EQ(inserted_parent.GetId(), parent.GetId()); + EXPECT_EQ(inserted_parent.GetName(), parent.GetName()); +} + +// TODO(fspreck) cognitive complexity > 25 (threshold) +TEST(test_entity, test_insert_with_property) { // NOLINT + auto transaction = caosdb::transaction::Transaction( + std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr)); + + auto entity = Entity(); + entity.SetId("entity_id"); + + auto prop = Property(); + prop.SetName("prop_name"); + prop.SetId("prop_id"); + prop.SetImportance("prop_importance"); + prop.SetValue("prop_value"); + prop.SetUnit("prop_unit"); + prop.SetDatatype("prop_dtype"); + + entity.AppendProperty(prop); + + transaction.InsertEntity(&entity); + + EXPECT_EQ(entity.GetProperties().Size(), 1); + + auto inserted_prop = entity.GetProperties().At(0); + + EXPECT_EQ(prop.GetName(), inserted_prop.GetName()); + EXPECT_EQ(prop.GetId(), inserted_prop.GetId()); + 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()); +} + // TODO(tf) cognitive complexity > 25 (threshold) TEST(test_entity, test_from_id_response) { // NOLINT IdResponse idResponse;