diff --git a/conanfile.py b/conanfile.py index 0e922bc74936d4341c8a574220c6149459ac6b7e..e2133cea76ce826d168364259d6a519ba28a75d3 100644 --- a/conanfile.py +++ b/conanfile.py @@ -3,7 +3,7 @@ from conans import ConanFile, CMake, tools class CaosdbConan(ConanFile): name = "caosdb" - version = "0.0.8" + version = "0.0.9" license = "AGPL-3.0-or-later" author = "Timm C. Fitschen <t.fitschen@indiscale.com>" url = "https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib.git" diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h index 6d542d4f885f38eea214763754e3bbfebe624286..d763bfe1683072c2571e7d9b728343d9ab8a913e 100644 --- a/include/caosdb/entity.h +++ b/include/caosdb/entity.h @@ -278,6 +278,9 @@ private: class Entity { public: Entity(); + inline Entity(const Entity &original) : Entity(CreateProtoEntity()) { + this->wrapped->CopyFrom(*original.wrapped); + }; explicit Entity(IdResponse *idResponse); explicit inline Entity(ProtoEntity *wrapped) : wrapped(wrapped) { errors.wrapped = this->wrapped->mutable_errors(); @@ -287,9 +290,12 @@ public: parents.wrapped = this->wrapped->mutable_parents(); }; - [[nodiscard]] inline auto GetId() const -> const std::string & { + [[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(); }; @@ -333,9 +339,7 @@ public: } auto SetRole(const std::string &role) -> void; - auto SetId(const std::string &id) -> void; auto SetName(const std::string &name) -> void; - auto SetVersionId(const std::string &id) -> void; // TODO(fspreck) ... and also these auto SetValue(const std::string &value) -> void; @@ -348,8 +352,12 @@ public: auto AppendParent(const Parent &parent) -> void; auto Switch(ProtoEntity *entity) -> void; -private: +protected: static auto CreateProtoEntity() -> ProtoEntity *; + auto SetId(const std::string &id) -> void; + auto SetVersionId(const std::string &id) -> void; + +private: ProtoEntity *wrapped; Properties properties; Parents parents; diff --git a/include/caosdb/status_code.h b/include/caosdb/status_code.h index 7d2f9f9572d4f6f2d44ef7ac167f760d9ce62534..7a4cd4185d45b5a1f93f271973018ff2c2c42126 100644 --- a/include/caosdb/status_code.h +++ b/include/caosdb/status_code.h @@ -35,6 +35,8 @@ namespace caosdb { enum StatusCode { + READY = -4, + GO_ON = -3, INITIAL = -2, EXECUTING = -1, SUCCESS = 0, @@ -48,6 +50,7 @@ enum StatusCode { TRANSACTION_STATUS_ERROR = 25, TRANSACTION_TYPE_ERROR = 26, UNSUPPORTED_FEATURE = 27, + ORIGINAL_ENTITY_MISSING_ID = 28, }; auto get_status_description(int code) -> const std::string &; diff --git a/include/caosdb/transaction.h b/include/caosdb/transaction.h index 80d77d13c610e999a61b3c452fefbb3222a2ff06..98148d6171306b2d35414934368568faf5bc2962 100644 --- a/include/caosdb/transaction.h +++ b/include/caosdb/transaction.h @@ -106,6 +106,35 @@ "wrong TransactionType.") \ } +/* + * Do all necessary checks and assure that another update can be added as a + * sub-request to a transaction. + */ +#define ASSERT_CAN_ADD_UPDATE \ + if (!IsStatus(TransactionStatus::INITIAL())) { \ + return StatusCode::TRANSACTION_STATUS_ERROR; \ + } \ + switch (this->transaction_type) { \ + case NONE: \ + this->transaction_type = TransactionType::INSERT; \ + case INSERT: \ + case MIXED_WRITE: \ + case MIXED_READ_AND_WRITE: \ + break; \ + default: \ + CAOSDB_LOG_ERROR_AND_RETURN_STATUS( \ + logger_name, StatusCode::TRANSACTION_TYPE_ERROR, \ + "You cannot add an update to this transaction because it has the " \ + "wrong TransactionType.") \ + } \ + if (!entity->HasId()) { \ + CAOSDB_LOG_ERROR_AND_RETURN_STATUS( \ + logger_name, StatusCode::ORIGINAL_ENTITY_MISSING_ID, \ + "You cannot update this entity without any id. Probably you did not " \ + "retrieve it first? Entity updates should always start with the " \ + "retrieval of the existing entity which may then be changed.") \ + } + namespace caosdb::transaction { using caosdb::entity::Entity; using ProtoEntity = caosdb::entity::v1alpha1::Entity; @@ -233,6 +262,16 @@ public: */ auto InsertEntity(Entity *entity) noexcept -> StatusCode; + /** + * Add the entity to this transaction for an update. + * + * The update is being processed when the Execute() or + * ExecuteAsynchronously() methods of this transaction are called. + * + * Changing the entity afterwards results in undefined behavior. + */ + auto UpdateEntity(Entity *entity) noexcept -> StatusCode; + /** * Add an entity id to this transaction for deletion. * diff --git a/src/caosdb/configuration.cpp b/src/caosdb/configuration.cpp index c24df459f5494232d2fadd2719a0be2d237b8f24..da598676d3256bd4a2a66f6dcbe2ff51c36a136d 100644 --- a/src/caosdb/configuration.cpp +++ b/src/caosdb/configuration.cpp @@ -37,6 +37,7 @@ #include "caosdb/utility.h" // for get_home_direc... #include <cassert> // for assert #include <cstdlib> // for getenv +#include <cstring> // IWYU pragma: keep #include <exception> // IWYU pragma: keep #include <grpcpp/security/credentials.h> // for SslCredentials #include <iterator> // for next diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp index 72b59e81cf51f86d2f7cb8fe3f482753af590da5..a74d2e61b24ad096b703f53ab6550a63dec2a362 100644 --- a/src/caosdb/transaction.cpp +++ b/src/caosdb/transaction.cpp @@ -42,8 +42,14 @@ namespace caosdb { auto get_status_description(int code) -> const std::string & { static const std::string MISSING_DESCRIPTION = "MISSING DESCRIPTION"; static const std::map<int, std::string> descriptions = { - {StatusCode::INITIAL, "The transaction has not been executed yet and " - "clients can stil change it."}, + {StatusCode::INITIAL, + "The transaction has just been intialized. It has not been executed yet " + "and clients can still change it and add sub-transactions."}, + {StatusCode::GO_ON, + "The transaction has a transaction_type yet and clients may add matching " + "sub-transaction or execute it right-away."}, + {StatusCode::READY, + "The transaction is ready for execution and cannot be changed anymore."}, {StatusCode::EXECUTING, "The transaction is currently being executed."}, {StatusCode::SUCCESS, "The action was successful"}, {StatusCode::CONNECTION_ERROR, @@ -68,6 +74,10 @@ auto get_status_description(int code) -> const std::string & { {StatusCode::TRANSACTION_TYPE_ERROR, "The Transaction has a transaction type which does not allow the " "attempted action."}, + {StatusCode::ORIGINAL_ENTITY_MISSING_ID, + "The attempt to update this entity failed because this entity does have " + "an id. This is the case when you did not retrieve it before applying any " + "changes and instantiated the Entity class explicitely."}, {StatusCode::UNSUPPORTED_FEATURE, "This feature is not available in the this client implementation."}}; try { @@ -142,6 +152,17 @@ auto Transaction::InsertEntity(Entity *entity) noexcept -> StatusCode { return StatusCode::INITIAL; } +auto Transaction::UpdateEntity(Entity *entity) noexcept -> StatusCode { + ASSERT_CAN_ADD_UPDATE + + auto *sub_request = this->request->add_requests(); + auto *proto_entity = sub_request->mutable_update_request(); + + entity->Switch(proto_entity); + + return StatusCode::INITIAL; +} + auto Transaction::Execute() -> TransactionStatus { ExecuteAsynchronously(); this->status.ThrowExceptionIfError(); @@ -221,6 +242,14 @@ auto Transaction::WaitForIt() const noexcept -> TransactionStatus { } this->result_set = std::make_unique<UniqueResult>(entity); } break; + case WrappedResponseCase::kUpdateResponse: { + auto *updatedIdResponse = responses->mutable_update_response(); + if (!updatedIdResponse->entity_errors().empty()) { + this->status = TransactionStatus::TRANSACTION_ERROR( + "The request returned with errors."); + } + this->result_set = std::make_unique<UniqueResult>(updatedIdResponse); + } break; case WrappedResponseCase::kInsertResponse: { auto *insertedIdResponse = responses->mutable_insert_response(); if (!insertedIdResponse->entity_errors().empty()) { diff --git a/test/test_entity.cpp b/test/test_entity.cpp index 1f3adf99b0702f7a369e20b42d2f8a3af3ea94dd..62e52abf3cccfedae4c9d581f2952fc5f5b9123f 100644 --- a/test/test_entity.cpp +++ b/test/test_entity.cpp @@ -61,16 +61,16 @@ TEST(test_entity, test_insert_entity) { std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr)); auto entity = Entity(); - entity.SetId("entity_id"); - entity.SetVersionId("version_id"); + entity.SetRole("entity_role"); + entity.SetName("entity_name"); - EXPECT_EQ(entity.GetId(), "entity_id"); - EXPECT_EQ(entity.GetVersionId(), "version_id"); + EXPECT_EQ(entity.GetRole(), "entity_role"); + EXPECT_EQ(entity.GetName(), "entity_name"); transaction.InsertEntity(&entity); - EXPECT_EQ(entity.GetId(), "entity_id"); - EXPECT_EQ(entity.GetVersionId(), "version_id"); + EXPECT_EQ(entity.GetRole(), "entity_role"); + EXPECT_EQ(entity.GetName(), "entity_name"); } // TODO(tf) cognitive complexity > 25 (threshold) @@ -83,7 +83,6 @@ TEST(test_entity, test_from_id_response) { // NOLINT Entity entity(&idResponse); - std::cout << entity.ToString() << std::endl; EXPECT_EQ(entity.GetId(), "entity_id"); EXPECT_EQ(entity.GetErrors().Size(), 1); EXPECT_EQ(entity.GetErrors().At(0).GetDescription(), "error_desc"); @@ -99,16 +98,19 @@ TEST(test_entity, test_from_id_response) { // NOLINT info->set_description("info_desc"); info->set_code(MessageCode::UNSPECIFIED); + std::cout << entity.ToString() << std::endl; Entity other_ent(&idr_warnings_and_infos); + std::cout << entity.ToString() << std::endl; EXPECT_EQ(other_ent.GetId(), "other_entity_id"); + std::cout << entity.ToString() << std::endl; EXPECT_EQ(other_ent.GetWarnings().Size(), 1); EXPECT_EQ(other_ent.GetWarnings().At(0).GetDescription(), "warning_desc"); EXPECT_EQ(other_ent.GetWarnings().At(0).GetCode(), MessageCode::ENTITY_HAS_NO_PROPERTIES); + std::cout << entity.ToString() << std::endl; EXPECT_EQ(other_ent.GetInfos().Size(), 1); EXPECT_EQ(other_ent.GetInfos().At(0).GetDescription(), "info_desc"); EXPECT_EQ(other_ent.GetInfos().At(0).GetCode(), MessageCode::UNSPECIFIED); } - } // namespace caosdb::entity diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp index b0f77d074d5d3bbcef2c05d020378992061a98d5..e17f34960438ce51f41e38da7ad7de4444e741dd 100644 --- a/test/test_transaction.cpp +++ b/test/test_transaction.cpp @@ -133,4 +133,17 @@ TEST(test_transaction, test_multi_result_set_three) { std::cout << rs.At(1).ToString(); } +TEST(test_transaction, test_update_entity) { + const auto *host = "localhost"; + auto configuration = InsecureConnectionConfiguration(host, 8000); + Connection connection(configuration); + auto transaction = connection.CreateTransaction(); + + caosdb::entity::Entity update_entity; + update_entity.SetRole("New role"); + auto error = transaction->UpdateEntity(&update_entity); + + EXPECT_EQ(error, StatusCode::ORIGINAL_ENTITY_MISSING_ID); +} + } // namespace caosdb::transaction