diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a79ad1944436beb30f82d1149dd1c840780a0287..8f77eea4848b735b3489d93238e3335e36cf04c5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -83,6 +83,9 @@ test: - mkdir build - cd build - conan install .. -s "compiler.libcxx=libstdc++11" + - FILE_TO_BE_PATCHED="$(grep "std::unique_ptr<ContextAllocator> context_allocator) {}" -l -r $(conan config home))" + - echo "FILE_TO_BE_PATCHED=$FILE_TO_BE_PATCHED" + - sed -e "s|std::unique_ptr<ContextAllocator> context_allocator) {}|std::unique_ptr<ContextAllocator> /*context_allocator*/) {}|" -i $FILE_TO_BE_PATCHED - cmake -DCMAKE_BUILD_TYPE=Debug .. - cmake --build . - cmake --build . --target unit_test_coverage diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 126c54ef2f7d4c26a43a163774c97b7c362643dd..542ad975f5241ce7755994b57afcf53e1cdeefc7 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -31,6 +31,7 @@ set(libcaosdb_INCL ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/log_level.h ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/logging.h ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/message_code.h + ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/protobuf_helper.h ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/status_code.h ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction.h ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction_status.h diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h index 9141e329c0634898490f9069a295854b2b3372a7..2819244f65214b8a444896b76e526cd10316fe24 100644 --- a/include/caosdb/entity.h +++ b/include/caosdb/entity.h @@ -28,18 +28,16 @@ #ifndef CAOSDB_ENTITY_H #define CAOSDB_ENTITY_H -#include "caosdb/message_code.h" -#include "caosdb/entity/v1alpha1/main.pb.h" // for Entity, RepeatedPtrField -#include "google/protobuf/util/json_util.h" -#include <memory> // for unique_ptr -#include <string> // for string +#include <string> // for string +#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... namespace caosdb::entity { +using caosdb::entity::v1alpha1::IdResponse; using ProtoParent = caosdb::entity::v1alpha1::Parent; using ProtoEntity = caosdb::entity::v1alpha1::Entity; -class Parents; - /** * Parent of an Entity. */ @@ -47,20 +45,26 @@ class Parent { public: explicit inline Parent(caosdb::entity::v1alpha1::Parent *wrapped) : wrapped(wrapped){}; - inline Parent() : wrapped(new ProtoParent){}; + Parent(); [[nodiscard]] auto GetId() const -> const std::string &; [[nodiscard]] auto GetName() const -> const std::string &; [[nodiscard]] auto GetDescription() const -> const std::string &; auto SetId(const std::string &id) -> void; auto SetName(const std::string &name) -> void; + 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 Parents; private: - auto AppendTo( - ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Parent> - *parents) const -> void; - std::shared_ptr<caosdb::entity::v1alpha1::Parent> wrapped; + static auto CreateProtoParent() -> ProtoParent *; + mutable caosdb::entity::v1alpha1::Parent *wrapped; }; /** @@ -175,11 +179,8 @@ private: */ class Entity { public: - inline Entity() : wrapped(new ProtoEntity) { - properties.wrapped = this->wrapped->mutable_properties(); - parents.wrapped = this->wrapped->mutable_parents(); - errors.wrapped = this->wrapped->mutable_errors(); - }; + Entity(); + explicit Entity(IdResponse *idResponse); explicit inline Entity(ProtoEntity *wrapped) : wrapped(wrapped) { errors.wrapped = this->wrapped->mutable_errors(); properties.wrapped = this->wrapped->mutable_properties(); @@ -189,7 +190,7 @@ public: [[nodiscard]] inline auto GetId() const -> const std::string & { return wrapped->id(); }; - [[nodiscard]] inline auto GetVersion() const -> const std::string & { + [[nodiscard]] inline auto GetVersionId() const -> const std::string & { return wrapped->version().id(); }; @@ -203,10 +204,10 @@ public: return wrapped->description(); }; - [[nodiscard]] auto GetDatatype() const -> const std::string & { + [[nodiscard]] inline auto GetDatatype() const -> const std::string & { return wrapped->datatype(); }; - [[nodiscard]] auto GetUnit() const -> const std::string & { + [[nodiscard]] inline auto GetUnit() const -> const std::string & { return wrapped->unit(); }; @@ -222,22 +223,24 @@ public: inline auto ToString() const -> const std::string { google::protobuf::util::JsonOptions options; std::string out; - google::protobuf::util::MessageToJsonString(*(this->wrapped.get()), &out, + google::protobuf::util::MessageToJsonString(*(this->wrapped), &out, options); return out; } auto SetId(const std::string &id) -> void; auto SetName(const std::string &name) -> void; - auto SetImportance(const std::string &importance) -> void; + auto SetVersionId(const std::string &id) -> void; auto SetValue(const std::string &value) -> void; auto SetUnit(const std::string &unit) -> void; auto SetDatatype(const std::string &datatype) -> void; auto AppendProperty(const Property &property) -> void; auto AppendParent(const Parent &parent) -> void; + auto Switch(ProtoEntity *entity) -> void; private: - std::unique_ptr<ProtoEntity> wrapped; + static auto CreateProtoEntity() -> ProtoEntity *; + ProtoEntity *wrapped; Properties properties; Parents parents; Messages errors; diff --git a/include/caosdb/exceptions.h b/include/caosdb/exceptions.h index cd6032358f1523ad27f8004e245ebc83e3454508..6dc44cbef4d05345eaab4b00d439967f7c88d89a 100644 --- a/include/caosdb/exceptions.h +++ b/include/caosdb/exceptions.h @@ -64,11 +64,21 @@ public: * @brief The transaction terminated unsuccessfully. */ class TransactionError : public GenericException { +protected: + TransactionError(StatusCode code, const std::string &what_arg) + : GenericException(code, what_arg) {} + public: explicit TransactionError(const std::string &what_arg) : GenericException(StatusCode::GENERIC_TRANSACTION_ERROR, what_arg) {} }; +class TransactionStatusError : public TransactionError { +public: + explicit TransactionStatusError(const std::string &what_arg) + : TransactionError(StatusCode::TRANSACTION_STATUS_ERROR, what_arg) {} +}; + /** * @brief Exception for errors of the ConfigurationManager or other components * of the configuration. diff --git a/include/caosdb/protobuf_helper.h b/include/caosdb/protobuf_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..ce7dd5ef7f58ce0fb1e72df08367426deb70cfca --- /dev/null +++ b/include/caosdb/protobuf_helper.h @@ -0,0 +1,34 @@ +/* + * 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_PROTOBUF_HELPER_H +#define CAOSDB_PROTOBUF_HELPER_H + +#include <google/protobuf/arena.h> + +namespace caosdb::utility { + +using google::protobuf::Arena; + +auto get_arena() -> Arena *; + +} // namespace caosdb::utility +#endif diff --git a/include/caosdb/status_code.h b/include/caosdb/status_code.h index 6fb8db3eea71a34038fa347df40f9be7ccbba70e..021f89388a699ecc6ba5da2f159f90b959995af9 100644 --- a/include/caosdb/status_code.h +++ b/include/caosdb/status_code.h @@ -45,6 +45,7 @@ enum StatusCode { GENERIC_TRANSACTION_ERROR = 22, CONFIGURATION_ERROR = 23, UNKNOWN_CONNECTION_ERROR = 24, + TRANSACTION_STATUS_ERROR = 25, }; auto get_status_description(int code) -> const std::string &; diff --git a/include/caosdb/transaction.h b/include/caosdb/transaction.h index 106e0d5fff92d6da3df9d3dd19ad7f05377d9235..cc7502c8439e36623a11d9c6602f4c433d14e687 100644 --- a/include/caosdb/transaction.h +++ b/include/caosdb/transaction.h @@ -24,23 +24,26 @@ /** * @brief Creation and execution of transactions. */ -#include <memory> // for shared_ptr, unique_ptr -#include <string> // for string #include "caosdb/entity.h" // for Entity #include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionSe... #include "caosdb/entity/v1alpha1/main.pb.h" // for Entity, RetrieveReq... #include "caosdb/transaction_status.h" // for TransactionStatus +#include "google/protobuf/util/json_util.h" // for MessageToJsonString, Jso... +#include <memory> // for shared_ptr, unique_ptr +#include <string> // for string namespace caosdb::transaction { using caosdb::entity::Entity; using ProtoEntity = caosdb::entity::v1alpha1::Entity; using caosdb::entity::v1alpha1::EntityTransactionService; -using caosdb::entity::v1alpha1::SingleRetrieveRequest; +using caosdb::entity::v1alpha1::IdResponse; +using caosdb::entity::v1alpha1::MultiTransactionRequest; +using caosdb::entity::v1alpha1::MultiTransactionResponse; using caosdb::transaction::TransactionStatus; class ResultSet { public: - virtual ~ResultSet(){}; + virtual ~ResultSet() = default; }; class UniqueResult : public ResultSet { @@ -48,6 +51,8 @@ public: ~UniqueResult(){}; explicit inline UniqueResult(ProtoEntity *protoEntity) : entity(new Entity(protoEntity)){}; + explicit inline UniqueResult(IdResponse *idResponse) + : entity(new Entity(idResponse)){}; [[nodiscard]] auto GetEntity() const -> const Entity &; private: @@ -59,16 +64,46 @@ private: */ class Transaction { private: - std::unique_ptr<ResultSet> result_set; + mutable std::unique_ptr<ResultSet> result_set; TransactionStatus status = TransactionStatus::INITIAL(); std::shared_ptr<EntityTransactionService::Stub> service_stub; - SingleRetrieveRequest request; // TODO(tf) + MultiTransactionRequest *request; + mutable MultiTransactionResponse *response; std::string error_message; public: Transaction(std::shared_ptr<EntityTransactionService::Stub> service_stub); + + /** + * Add an entity id to this transaction for retrieval. + * + * The retrieval is being processed when the Execute() or + * ExecuteAsynchronously() methods of this transaction are called. + */ auto RetrieveById(const std::string &id) -> void; + /** + * Add the entity to this transaction for an insertion. + * + * The insertion is being processed when the Execute() or + * ExecuteAsynchronously() methods of this transaction are called. + * + * Changing the entity afterwards results in undefined behavior. + */ + auto InsertEntity(Entity *entity) -> void; + + /** + * Add an entity id to this transaction for deletion. + * + * The deletion is being processed when the Execute() or + * ExecuteAsynchronously() methods of this transaction are called. + */ + auto DeleteById(const std::string &id) -> void; + + inline auto IsStatus(const TransactionStatus &status) const noexcept -> bool { + return this->status.GetCode() == status.GetCode(); + }; + /** * Execute this transaction in blocking mode and return the status. */ @@ -102,6 +137,13 @@ public: const ResultSet *result_set = this->result_set.get(); return *result_set; } + + inline auto RequestToString() const -> const std::string { + google::protobuf::util::JsonOptions options; + std::string out; + google::protobuf::util::MessageToJsonString(*this->request, &out, options); + return out; + } }; } // namespace caosdb::transaction diff --git a/proto b/proto index 72306a73676e6880a7164108ab0ab17b8978f7e1..d6f2197731060a66934a9d45825cc9635a31131a 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 72306a73676e6880a7164108ab0ab17b8978f7e1 +Subproject commit d6f2197731060a66934a9d45825cc9635a31131a diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 67074f762573c6e8b5eb7be34a728370e7dcd6ce..95b2dabac276216bd744ea347a71f986070fce8f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,7 @@ set(libcaosdb_SRC ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/logging.cpp ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/connection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/configuration.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/protobuf_helper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction.cpp ) diff --git a/src/caosdb/entity.cpp b/src/caosdb/entity.cpp index 65a641e1a58647eb8a660b2cba080bbf12f14f1e..c4bd56575e204ea97b4b2b84528c3ae452e6362e 100644 --- a/src/caosdb/entity.cpp +++ b/src/caosdb/entity.cpp @@ -18,33 +18,45 @@ * */ #include "caosdb/entity.h" -#include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionS... -#include "caosdb/entity/v1alpha1/main.pb.h" // for SingleRetrieveRequest -#include <cassert> // for assert -#include <map> // for map -#include <memory> // for allocator, unique_ptr -#include <stdexcept> // for out_of_range -#include <utility> // for move +#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 namespace caosdb::entity { +using caosdb::entity::v1alpha1::IdResponse; +using ProtoParent = caosdb::entity::v1alpha1::Parent; +using ProtoEntity = caosdb::entity::v1alpha1::Entity; +using caosdb::utility::get_arena; + +Parent::Parent() : wrapped(Parent::CreateProtoParent()) {} + +auto Parent::CreateProtoParent() -> ProtoParent * { + return google::protobuf::Arena::CreateMessage<ProtoParent>(get_arena()); +} + auto Parent::SetName(const std::string &name) -> void { this->wrapped->set_name(name); } auto Parent::SetId(const std::string &id) -> void { this->wrapped->set_id(id); } -auto Parent::AppendTo( - ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Parent> - *parents) const -> void { - auto *destination = parents->Add(); - destination = std::move(this->wrapped.get()); +[[nodiscard]] auto Parent::GetId() const -> const std::string & { + return this->wrapped->id(); +} + +[[nodiscard]] auto Parent::GetName() const -> const std::string & { + return this->wrapped->name(); } auto Parents::Append(const Parent &parent) -> void { auto *destination = this->wrapped->Add(); - destination = std::move(parent.wrapped.get()); + destination->Swap(parent.wrapped); + + // Clear the originally wrapped object and return it to the Arena + parent.wrapped->Clear(); - // parent.AppendTo(this->wrapped); + // set the pointer to the new object which is owned by the RepeatedPtrField + parent.wrapped = destination; } [[nodiscard]] auto Entity::GetParents() const -> const Parents & { @@ -54,4 +66,32 @@ auto Parents::Append(const Parent &parent) -> void { auto Entity::AppendParent(const Parent &parent) -> void { this->parents.Append(parent); } + +auto Entity::CreateProtoEntity() -> ProtoEntity * { + return google::protobuf::Arena::CreateMessage<ProtoEntity>(get_arena()); +} + +Entity::Entity() : wrapped(Entity::CreateProtoEntity()) { + properties.wrapped = this->wrapped->mutable_properties(); + parents.wrapped = this->wrapped->mutable_parents(); + errors.wrapped = this->wrapped->mutable_errors(); +} + +Entity::Entity(IdResponse *idResponse) : Entity() { + this->wrapped->set_id(idResponse->id()); + this->wrapped->mutable_errors()->Swap(idResponse->mutable_entity_errors()); +} + +auto Entity::SetId(const std::string &id) -> void { this->wrapped->set_id(id); } + +auto Entity::SetVersionId(const std::string &id) -> void { + this->wrapped->mutable_version()->set_id(id); +} + +auto Entity::Switch(ProtoEntity *entity) -> void { + this->wrapped->Swap(entity); + this->wrapped->Clear(); + this->wrapped = entity; +} + } // namespace caosdb::entity diff --git a/src/caosdb/protobuf_helper.cpp b/src/caosdb/protobuf_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a9ad000595285f1c0fb6402182e0d48294daa37d --- /dev/null +++ b/src/caosdb/protobuf_helper.cpp @@ -0,0 +1,33 @@ +/* + * 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/>. + * + */ +#include "caosdb/protobuf_helper.h" +#include <google/protobuf/arena.h> + +namespace caosdb::utility { + +using google::protobuf::Arena; + +auto get_arena() -> Arena * { + static Arena arena; + return &arena; +} + +} // namespace caosdb::utility diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp index 302eb48e6e691b432aa58ae69e617a2d45eaaca9..71e9300d4ebd2290202263d6a930a78c895dd8d7 100644 --- a/src/caosdb/transaction.cpp +++ b/src/caosdb/transaction.cpp @@ -20,7 +20,10 @@ #include "caosdb/transaction.h" #include "caosdb/entity/v1alpha1/main.grpc.pb.h" // for EntityTransactionS... #include "caosdb/entity/v1alpha1/main.pb.h" // for SingleRetrieveRequest +#include "caosdb/exceptions.h" // for TransactionError, ... +#include "caosdb/protobuf_helper.h" // for get_arena #include "caosdb/status_code.h" // for StatusCode, AUTHEN... +#include "google/protobuf/arena.h" // for Arena #include "grpcpp/grpcpp.h" // for CompletionQueue #include "grpcpp/impl/codegen/async_unary_call.h" // for ClientAsyncRespons... #include "grpcpp/impl/codegen/client_context.h" // for ClientContext @@ -59,7 +62,9 @@ auto get_status_description(int code) -> const std::string & { {StatusCode::CONFIGURATION_ERROR, "An error occurred during the configuration of the ConfigurationManager."}, {StatusCode::UNKNOWN_CONNECTION_ERROR, - "The ConnectionManager does not know any connection of this name."}}; + "The ConnectionManager does not know any connection of this name."}, + {StatusCode::TRANSACTION_STATUS_ERROR, + "The Transaction is in a wrong state for the attempted action."}}; try { return descriptions.at(code); } catch (const std::out_of_range &exc) { @@ -71,8 +76,13 @@ auto get_status_description(int code) -> const std::string & { namespace caosdb::transaction { using caosdb::entity::v1alpha1::EntityTransactionService; -using caosdb::entity::v1alpha1::SingleRetrieveRequest; -using caosdb::entity::v1alpha1::SingleRetrieveResponse; +using caosdb::entity::v1alpha1::MultiTransactionRequest; +using caosdb::entity::v1alpha1::MultiTransactionResponse; +using WrappedResponseCase = + caosdb::entity::v1alpha1::TransactionResponse::WrappedResponseCase; +using caosdb::exceptions::TransactionError; +using caosdb::exceptions::TransactionStatusError; +using caosdb::utility::get_arena; using grpc::ClientAsyncResponseReader; using ProtoEntity = caosdb::entity::v1alpha1::Entity; using grpc::CompletionQueue; @@ -83,16 +93,63 @@ using grpc::CompletionQueue; } Transaction::Transaction( - std::shared_ptr<EntityTransactionService::Stub> service_stub) { + std::shared_ptr<EntityTransactionService::Stub> service_stub) + : request(google::protobuf::Arena::CreateMessage<MultiTransactionRequest>( + get_arena())), + response(google::protobuf::Arena::CreateMessage<MultiTransactionResponse>( + get_arena())) { this->service_stub = std::move(service_stub); } auto Transaction::RetrieveById(const std::string &id) -> void { - SingleRetrieveRequest request; - // this copies the id, so we're safe. - request.mutable_retrieve_request()->set_id(id); + if (!IsStatus(TransactionStatus::INITIAL())) { + throw TransactionStatusError( + caosdb::get_status_description(StatusCode::TRANSACTION_STATUS_ERROR)); + } + + // TODO(tf) remove checks when the server is ready + if (this->request->requests_size() > 0) { + throw TransactionError( + "This request object cannot handle another RetrieveById sub-request"); + } + + auto *sub_request = this->request->add_requests(); + sub_request->mutable_retrieve_request()->set_id(id); +} + +auto Transaction::DeleteById(const std::string &id) -> void { + if (!IsStatus(TransactionStatus::INITIAL())) { + throw TransactionStatusError( + caosdb::get_status_description(StatusCode::TRANSACTION_STATUS_ERROR)); + } + + // TODO(tf) remove checks when the server is ready + if (this->request->requests_size() > 0) { + throw TransactionError( + "This request object cannot handle another DeleteById sub-request"); + } - this->request = request; + auto *sub_request = this->request->add_requests(); + sub_request->mutable_delete_request()->set_id(id); +} + +auto Transaction::InsertEntity(Entity *entity) -> void { + if (!IsStatus(TransactionStatus::INITIAL())) { + throw TransactionStatusError( + caosdb::get_status_description(StatusCode::TRANSACTION_STATUS_ERROR)); + } + + // TODO(tf) remove checks when the server is ready + if (this->request->requests_size() > 0) { + throw TransactionError( + "This request object cannot handle another DeleteById sub-request"); + } + + auto *sub_request = this->request->add_requests(); + auto *proto_entity = sub_request->mutable_insert_request()->mutable_entity(); + + // swap and switch + entity->Switch(proto_entity); } auto Transaction::Execute() -> TransactionStatus { @@ -106,17 +163,16 @@ auto Transaction::ExecuteAsynchronously() noexcept -> void { grpc::Status grpc_status; CompletionQueue cq; - SingleRetrieveResponse response; grpc::ClientContext context; - std::unique_ptr<ClientAsyncResponseReader<SingleRetrieveResponse>> rpc( - this->service_stub->PrepareAsyncSingleRetrieve(&context, this->request, - &cq)); + std::unique_ptr<ClientAsyncResponseReader<MultiTransactionResponse>> rpc( + this->service_stub->PrepareAsyncMultiTransaction(&context, *(this->request), + &cq)); rpc->StartCall(); int tag = 1; void *send_tag = static_cast<void *>(&tag); - rpc->Finish(&response, &grpc_status, send_tag); + rpc->Finish(this->response, &grpc_status, send_tag); void *recv_tag = nullptr; bool ok = false; @@ -141,12 +197,32 @@ auto Transaction::ExecuteAsynchronously() noexcept -> void { } else { this->status = TransactionStatus::SUCCESS(); } - - auto *entity = response.mutable_retrieve_response()->release_entity(); - this->result_set = std::make_unique<UniqueResult>(entity); } auto Transaction::WaitForIt() const noexcept -> TransactionStatus { + if (this->response->responses_size() == 1) { + auto *responses = this->response->mutable_responses(0); + switch (responses->wrapped_response_case()) { + case WrappedResponseCase::kRetrieveResponse: { + auto *entity = responses->mutable_retrieve_response()->release_entity(); + this->result_set = std::make_unique<UniqueResult>(entity); + } break; + case WrappedResponseCase::kInsertResponse: { + auto *insertedIdResponse = responses->mutable_insert_response(); + this->result_set = std::make_unique<UniqueResult>(insertedIdResponse); + } break; + case WrappedResponseCase::kDeleteResponse: { + auto *deletedIdResponse = responses->mutable_delete_response(); + this->result_set = std::make_unique<UniqueResult>(deletedIdResponse); + } break; + default: + // TODO(tf) + break; + } + } else { + // TODO(tf) + } + return this->status; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4eb876b8a11d98cd74023d41300981d11eff26ec..6a9216af93b4106461438836965f6a70ebfee88d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,6 +24,7 @@ set(test_cases test_connection test_entity test_info + test_protobuf test_transaction test_utility test_ccaosdb diff --git a/test/test_entity.cpp b/test/test_entity.cpp index ff0fa68db3e36230f3c02df160057c38f2114075..fa552e830489b3e1b2ab61771782381107c2ec9d 100644 --- a/test/test_entity.cpp +++ b/test/test_entity.cpp @@ -19,28 +19,74 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ -#include "caosdb/entity.h" // for Entity -#include "caosdb/entity/v1alpha1/main.pb.h" // for Entity -#include "caosdb/exceptions.h" // for ConnectionError -#include "caosdb/transaction.h" // for Transaction, UniqueResult -#include "caosdb/transaction_status.h" // for ConnectionError -#include "caosdb_test_utility.h" // for EXPECT_THROW_MESSAGE -#include "gtest/gtest-message.h" // for Message -#include "gtest/gtest-test-part.h" // for SuiteApiResolver, TestPa... -#include "gtest/gtest_pred_impl.h" // for Test, TestInfo, TEST -#include <memory> // for allocator, unique_ptr +#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/transaction.h" // for Transaction +#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 <iostream> // for endl, basic_ostream +#include <memory> // for allocator, shared_ptr +#include <string> // for operator<< namespace caosdb::entity { +using caosdb::entity::v1alpha1::IdResponse; -TEST(test_entity, test_append_parent) { +TEST(test_entity, test_parent_setters) { auto parent = Parent(); parent.SetName("RT1"); parent.SetId("some-id"); + EXPECT_EQ(parent.GetName(), "RT1"); + EXPECT_EQ(parent.GetId(), "some-id"); +} + +TEST(test_entity, test_append_parent) { + auto parent = Parent(); + parent.SetId("some-id"); + auto entity = Entity(); EXPECT_EQ(entity.GetParents().Size(), 0); entity.AppendParent(parent); EXPECT_EQ(entity.GetParents().Size(), 1); + + auto same_parent = entity.GetParents().At(0); + EXPECT_EQ(same_parent.GetId(), "some-id"); +} + +TEST(test_entity, test_insert_entity) { + auto transaction = caosdb::transaction::Transaction( + std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr)); + + auto entity = Entity(); + entity.SetId("entity_id"); + entity.SetVersionId("version_id"); + + EXPECT_EQ(entity.GetId(), "entity_id"); + EXPECT_EQ(entity.GetVersionId(), "version_id"); + + transaction.InsertEntity(&entity); + + EXPECT_EQ(entity.GetId(), "entity_id"); + EXPECT_EQ(entity.GetVersionId(), "version_id"); +} + +TEST(test_entity, test_from_id_response) { + IdResponse idResponse; + idResponse.set_id("entity_id"); + auto *error = idResponse.add_entity_errors(); + error->set_code(1234); + error->set_description("error_desc"); + + 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"); + EXPECT_EQ(entity.GetErrors().At(0).GetDescription(), "wrong"); + EXPECT_EQ(entity.GetErrors().At(0).GetCode(), 1234); } } // namespace caosdb::entity diff --git a/test/test_protobuf.cpp b/test/test_protobuf.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9dc957a2f85cd95ce3ead5f59ab8c10868582003 --- /dev/null +++ b/test/test_protobuf.cpp @@ -0,0 +1,84 @@ +/* + * + * 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/>. + * + */ +#include "caosdb/entity/v1alpha1/main.pb.h" // for RepeatedPtrField, Message +#include "gtest/gtest-message.h" // for Message +#include "gtest/gtest-test-part.h" // for SuiteApiResolver, TestPa... +#include "gtest/gtest_pred_impl.h" // for Test, TestInfo, TEST +#include <memory> // for allocator + +namespace caosdb { +using caosdb::entity::v1alpha1::Entity; +using caosdb::entity::v1alpha1::Message; + +TEST(test_protobuf, test_swap_trivial) { + Message message_source; + message_source.set_code(1234); + message_source.set_description("desc"); + + Message message_destination; + + EXPECT_EQ(message_source.code(), 1234); + EXPECT_EQ(message_source.description(), "desc"); + EXPECT_EQ(message_destination.code(), 0); + EXPECT_EQ(message_destination.description(), ""); + + message_source.Swap(&message_destination); + + EXPECT_EQ(message_source.code(), 0); + EXPECT_EQ(message_source.description(), ""); + EXPECT_EQ(message_destination.code(), 1234); + EXPECT_EQ(message_destination.description(), "desc"); +} + +TEST(test_protobuf, test_swap_nested) { + Entity entity_source; + entity_source.set_id("entity_id"); + auto *version_source = entity_source.mutable_version(); + version_source->set_id("version_id"); + + Entity entity_destination; + auto *version_destination = entity_destination.mutable_version(); + + EXPECT_EQ(entity_source.id(), "entity_id"); + EXPECT_EQ(entity_source.version().id(), "version_id"); + EXPECT_EQ(version_source->id(), "version_id"); + EXPECT_EQ(entity_destination.id(), ""); + EXPECT_EQ(entity_destination.version().id(), ""); + EXPECT_EQ(version_destination->id(), ""); + + entity_source.Swap(&entity_destination); + + EXPECT_EQ(entity_source.id(), ""); + EXPECT_EQ(entity_source.version().id(), ""); + EXPECT_EQ(entity_destination.id(), "entity_id"); + EXPECT_EQ(entity_destination.version().id(), "version_id"); + + // has not been swapped! + EXPECT_EQ(version_source->id(), "version_id"); + EXPECT_EQ(version_destination->id(), ""); + + // Member pointers to nested messages have been swapped + EXPECT_EQ(entity_source.mutable_version(), version_destination); + EXPECT_EQ(entity_destination.mutable_version(), version_source); +} + +} // namespace caosdb diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp index c2c76969580f7d915fd6bb4555fa32a1f91e11bb..ab1979f7e455d8b1b9fd7e8d672bd1b8ab1f5775 100644 --- a/test/test_transaction.cpp +++ b/test/test_transaction.cpp @@ -24,13 +24,14 @@ #include "caosdb/entity.h" // for Entity #include "caosdb/entity/v1alpha1/main.pb.h" // for Entity #include "caosdb/exceptions.h" // for ConnectionError -#include "caosdb/transaction.h" // for Transaction, UniqueResult -#include "caosdb/transaction_status.h" // for ConnectionError -#include "caosdb_test_utility.h" // for EXPECT_THROW_MESSAGE -#include "gtest/gtest-message.h" // for Message -#include "gtest/gtest-test-part.h" // for SuiteApiResolver, TestPa... -#include "gtest/gtest_pred_impl.h" // for Test, TestInfo, TEST -#include <memory> // for allocator, unique_ptr +#include "caosdb/status_code.h" +#include "caosdb/transaction.h" // for Transaction, UniqueResult +#include "caosdb/transaction_status.h" // for ConnectionError +#include "caosdb_test_utility.h" // for EXPECT_THROW_MESSAGE +#include "gtest/gtest-message.h" // for Message +#include "gtest/gtest-test-part.h" // for SuiteApiResolver, TestPa... +#include "gtest/gtest_pred_impl.h" // for Test, TestInfo, TEST +#include <memory> // for allocator, unique_ptr namespace caosdb::transaction { using caosdb::configuration::InsecureConnectionConfiguration; @@ -64,4 +65,17 @@ TEST(test_transaction, unique_result) { // delete entity; } +TEST(test_transaction, test_unavailable) { + const auto *host = "localhost"; + auto configuration = InsecureConnectionConfiguration(host, 8000); + Connection connection(configuration); + auto transaction = connection.CreateTransaction(); + + transaction->RetrieveById("100"); + transaction->ExecuteAsynchronously(); + + auto status = transaction->WaitForIt(); + EXPECT_EQ(status.GetCode(), StatusCode::CONNECTION_ERROR); +} + } // namespace caosdb::transaction