diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h index eb93b36875bfe7778345152bcbb53777d9509759..67dd71a563caa9d8c0372542e92f3b8a971b3b0e 100644 --- a/include/caosdb/entity.h +++ b/include/caosdb/entity.h @@ -33,6 +33,7 @@ #include "caosdb/entity/v1alpha1/main.pb.h" // for RepeatedPtrField, Message #include "caosdb/message_code.h" // for get_message_code, Messag... #include "google/protobuf/util/json_util.h" // for MessageToJsonString, Jso... +#include "boost/filesystem/path.hpp" // for path namespace caosdb::entity { using caosdb::entity::v1alpha1::IdResponse; @@ -41,6 +42,20 @@ using ProtoProperty = caosdb::entity::v1alpha1::Property; using ProtoEntity = caosdb::entity::v1alpha1::Entity; using caosdb::entity::v1alpha1::FileTransmissionId; +class FileDescriptor { +public: + auto GetEntityId() const -> const std::string &; + auto GetLocalPath() const -> const boost::filesystem::path &; + auto GetRemotePath() const -> const std::string &; + auto GetSize() const -> long long; + +private: + std::string entity_id; + std::string remote_path; + std::string local_path; + long long size; +}; + /** * Messages convey information about the state and result of transactions. * diff --git a/include/caosdb/transaction.h b/include/caosdb/transaction.h index 1b89ecbec2c4e7c15bbe9739809ee0ee1af04cac..a6d2be0616e08ef346a88e2d258c812616f24af0 100644 --- a/include/caosdb/transaction.h +++ b/include/caosdb/transaction.h @@ -33,10 +33,11 @@ #include "google/protobuf/util/json_util.h" // for MessageToJsonString, Jso... #include <iterator> // IWYU pragma: no_include <ext/alloc_traits.h> -#include <memory> // for shared_ptr, unique_ptr -#include <stdexcept> -#include <string> // for string -#include <vector> // for vector +#include <memory> // for shared_ptr, unique_ptr +#include <stdexcept> // for out_of_range +#include <string> // for string +#include <utility> // for move +#include <vector> // for vector /** * Do all necessary checks and assure that another retrieval (by id or by @@ -158,6 +159,7 @@ */ namespace caosdb::transaction { using caosdb::entity::Entity; +using caosdb::entity::FileDescriptor; using ProtoEntity = caosdb::entity::v1alpha1::Entity; using caosdb::entity::v1alpha1::EntityTransactionService; using caosdb::entity::v1alpha1::FileDownloadResponse; @@ -181,49 +183,98 @@ static const std::string logger_name = "caosdb::transaction"; /** * Abstract base class for the results of a Transaction. */ -class ResultSet { +template <class T> class ResultSet { class iterator; public: virtual ~ResultSet() = default; [[nodiscard]] virtual auto Size() const noexcept -> int = 0; - [[nodiscard]] virtual auto At(const int index) const -> const Entity & = 0; + [[nodiscard]] virtual auto At(const int index) const -> const T & = 0; auto begin() const -> iterator; auto end() const -> iterator; private: - class iterator : public std::iterator<std::output_iterator_tag, Entity> { + class iterator : public std::iterator<std::output_iterator_tag, T> { public: - explicit iterator(const ResultSet *result_set, int index = 0); - auto operator*() const -> const Entity &; + explicit iterator(const ResultSet<T> *result_set, int index = 0); + auto operator*() const -> const T &; auto operator++() -> iterator &; auto operator++(int) -> iterator; auto operator!=(const iterator &rhs) const -> bool; private: int current_index = 0; - const ResultSet *result_set; + const ResultSet<T> *result_set; }; }; +template <class T> +ResultSet<T>::iterator::iterator(const ResultSet *result_set_param, int index) + : current_index(index), result_set(result_set_param) {} + +template <class T> auto ResultSet<T>::iterator::operator*() const -> const T & { + return this->result_set->At(current_index); +} + +template <class T> +auto ResultSet<T>::iterator::operator++() -> ResultSet<T>::iterator & { + current_index++; + return *this; +} + +template <class T> +auto ResultSet<T>::iterator::operator++(int) -> ResultSet<T>::iterator { + iterator tmp(*this); + operator++(); + return tmp; +} + +template <class T> +auto ResultSet<T>::iterator::operator!=(const iterator &rhs) const -> bool { + return this->current_index != rhs.current_index; +} + +template <class T> auto ResultSet<T>::begin() const -> ResultSet<T>::iterator { + return ResultSet<T>::iterator(this, 0); +} + +template <class T> auto ResultSet<T>::end() const -> ResultSet<T>::iterator { + return ResultSet<T>::iterator(this, Size()); +} + +template <class T> class IMultiResultSet : public ResultSet<T> { +public: + virtual ~IMultiResultSet() = default; + inline explicit IMultiResultSet(std::vector<std::unique_ptr<T>> result_set) + : items(std::move(result_set)) {} + [[nodiscard]] inline auto Size() const noexcept -> int override { + return this->items.size(); + } + [[nodiscard]] inline auto At(const int index) const -> const T & override { + return *(this->items.at(index)); + } + +protected: + std::vector<std::unique_ptr<T>> items; +}; + +class FilesResultSet : public IMultiResultSet<FileDescriptor> { +public: + ~FilesResultSet() = default; + explicit FilesResultSet( + std::vector<std::unique_ptr<FileDescriptor>> result_set); +}; + /** * Container with results of a transaction. * * In contrast to UniqueResult, this one can also hold multiple entities or zero * entities. */ -class MultiResultSet : public ResultSet { +class MultiResultSet : public IMultiResultSet<Entity> { public: ~MultiResultSet() = default; explicit MultiResultSet(std::vector<std::unique_ptr<Entity>> result_set); - [[nodiscard]] inline auto Size() const noexcept -> int override { - return this->entities.size(); - } - [[nodiscard]] inline auto At(const int index) const - -> const Entity & override { - return *(this->entities.at(index)); - } - std::vector<std::unique_ptr<Entity>> entities; }; /** @@ -232,7 +283,7 @@ public: * In contrast to MultiResultSet, this one guarantees to hold exactly one * entity. */ -class UniqueResult : public ResultSet { +class UniqueResult : public ResultSet<Entity> { public: ~UniqueResult() = default; explicit inline UniqueResult(ProtoEntity *protoEntity) @@ -285,6 +336,29 @@ public: Transaction(std::shared_ptr<EntityTransactionService::Stub> entity_service, std::shared_ptr<FileTransmissionService::Stub> file_service); + /** + * Add an entity id to this transaction for retrieval and also download the + * file. + * + * If the entity doesn't have a file a warning is appended. + * + * If the file cannot be downloaded due to unsufficient permissions an error + * is appended. + */ + auto RetrieveAndDownloadFilesById(const std::string &id) noexcept + -> StatusCode; + auto DownloadFilesById(const std::string &path) noexcept -> StatusCode; + auto RetrieveAndDownloadFilesByQuery(const std::string &query) noexcept + -> StatusCode; + auto DownloadFilesByQuery(const std::string &query) noexcept -> StatusCode; + + /** + * Return a ResultSet<FileDescriptor>. + * + * The file result set is empty until the transaction terminates. + */ + auto GetDownloadedFiles() const noexcept -> const FilesResultSet &; + /** * Add an entity id to this transaction for retrieval. * @@ -364,12 +438,13 @@ public: /** * Return the current status of the transaction. */ - [[nodiscard]] inline auto GetStatus() const -> TransactionStatus { + [[nodiscard]] inline auto GetStatus() const noexcept -> TransactionStatus { return this->status; } - [[nodiscard]] inline auto GetResultSet() const -> const ResultSet & { - const ResultSet *result_set = this->result_set.get(); + [[nodiscard]] inline auto GetResultSet() const noexcept + -> const ResultSet<Entity> & { + const ResultSet<Entity> *result_set = this->result_set.get(); return *result_set; } @@ -380,7 +455,7 @@ public: * this transaction. In all other cases, the return value will be * -1. */ - [[nodiscard]] inline auto GetCountResult() const -> long { + [[nodiscard]] inline auto GetCountResult() const noexcept -> long { return query_count; } @@ -421,7 +496,7 @@ public: private: bool has_query = false; TransactionType transaction_type = TransactionType::NONE; - mutable std::unique_ptr<ResultSet> result_set; + mutable std::unique_ptr<ResultSet<Entity>> result_set; mutable TransactionStatus status = TransactionStatus::INITIAL(); std::shared_ptr<EntityTransactionService::Stub> entity_service; std::shared_ptr<FileTransmissionService::Stub> file_service; diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp index e6b22583f9a018faa048e688f4824dc25cd81938..e2c524343693a35c22fe3b7dee3253a80bebcfdc 100644 --- a/src/caosdb/transaction.cpp +++ b/src/caosdb/transaction.cpp @@ -112,38 +112,8 @@ using grpc::ClientAsyncResponseReader; using ProtoEntity = caosdb::entity::v1alpha1::Entity; using grpc::CompletionQueue; -ResultSet::iterator::iterator(const ResultSet *result_set_param, int index) - : current_index(index), result_set(result_set_param) {} - -auto ResultSet::iterator::operator*() const -> const Entity & { - return this->result_set->At(current_index); -} - -auto ResultSet::iterator::operator++() -> ResultSet::iterator & { - current_index++; - return *this; -} - -auto ResultSet::iterator::operator++(int) -> ResultSet::iterator { - iterator tmp(*this); - operator++(); - return tmp; -} - -auto ResultSet::iterator::operator!=(const iterator &rhs) const -> bool { - return this->current_index != rhs.current_index; -} - -auto ResultSet::begin() const -> ResultSet::iterator { - return ResultSet::iterator(this, 0); -} - -auto ResultSet::end() const -> ResultSet::iterator { - return ResultSet::iterator(this, Size()); -} - MultiResultSet::MultiResultSet(std::vector<std::unique_ptr<Entity>> result_set) - : entities(std::move(result_set)) {} + : IMultiResultSet<Entity>(std::move(result_set)) {} [[nodiscard]] auto UniqueResult::GetEntity() const -> const Entity & { const Entity *result = this->entity.get(); @@ -160,6 +130,9 @@ Transaction::Transaction( this->entity_service = std::move(entity_service); this->file_service = std::move(file_service); this->query_count = -1; +} + +auto Transaction::RetrieveById(const std::string &id) noexcept -> StatusCode { ASSERT_CAN_ADD_RETRIEVAL auto *sub_request = this->request->add_requests(); diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp index 6e04976d7d203e29c0ba084896e2f897c6121d48..b062cb8a6372ac5db749cc9b6acd998f8829a14a 100644 --- a/test/test_transaction.cpp +++ b/test/test_transaction.cpp @@ -182,7 +182,7 @@ TEST(test_transaction, test_multi_deletion) { auto configuration = InsecureConnectionConfiguration(host, 8000); Connection connection(configuration); auto transaction = connection.CreateTransaction(); - EXPECT_EQ(transaction.GetStatus().GetCode(), StatusCode::INITIAL); + EXPECT_EQ(transaction->GetStatus().GetCode(), StatusCode::INITIAL); for (int i = 0; i < 3; i++) { auto status = transaction->DeleteById("asdf"); EXPECT_EQ(status, StatusCode::GO_ON);