From f74d19f4748d1ebc2dcaf716c60803f381313e55 Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Tue, 3 Aug 2021 23:47:54 +0200
Subject: [PATCH] WIP: files

---
 CMakeLists.txt               |  2 +-
 conanfile.py                 |  2 +-
 include/caosdb/connection.h  |  4 ++++
 include/caosdb/transaction.h |  9 +++++--
 proto                        |  2 +-
 src/caosdb/connection.cpp    |  8 +++++--
 src/caosdb/transaction.cpp   | 46 +++++++++++++++++++++++++++++-------
 test/test_entity.cpp         | 12 ++++++----
 8 files changed, 66 insertions(+), 19 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index b786aa7..790ad4b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,7 +20,7 @@
 
 cmake_minimum_required(VERSION 3.13)
 
-set(libcaosdb_VERSION 0.0.9)
+set(libcaosdb_VERSION 0.0.10)
 set(libcaosdb_COMPATIBLE_SERVER_VERSION_MAJOR 0)
 set(libcaosdb_COMPATIBLE_SERVER_VERSION_MINOR 5)
 set(libcaosdb_COMPATIBLE_SERVER_VERSION_PATCH 0)
diff --git a/conanfile.py b/conanfile.py
index e2133ce..0e31f8d 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.9"
+    version = "0.0.10"
     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/connection.h b/include/caosdb/connection.h
index 5d7930a..ef9597b 100644
--- a/include/caosdb/connection.h
+++ b/include/caosdb/connection.h
@@ -45,6 +45,7 @@ using boost::filesystem::path;
 using caosdb::authentication::Authenticator;
 using caosdb::configuration::ConnectionConfiguration;
 using caosdb::entity::v1alpha1::EntityTransactionService;
+using caosdb::entity::v1alpha1::FileTransmissionService;
 using caosdb::info::VersionInfo;
 using caosdb::info::v1alpha1::GeneralInfoService;
 using caosdb::transaction::Transaction;
@@ -104,6 +105,9 @@ private:
   /// Service for entity transactions. We use a shared pointer because
   /// Transaction instances also own this service stub.
   std::shared_ptr<EntityTransactionService::Stub> entity_transaction_service;
+  /// Service for file transmission (download and upload). We use a shared
+  /// pointer because Transaction instances also own this service stub.
+  std::shared_ptr<FileTransmissionService::Stub> file_transmission_service;
 };
 
 /**
diff --git a/include/caosdb/transaction.h b/include/caosdb/transaction.h
index 98148d6..ce7eb42 100644
--- a/include/caosdb/transaction.h
+++ b/include/caosdb/transaction.h
@@ -139,9 +139,11 @@ namespace caosdb::transaction {
 using caosdb::entity::Entity;
 using ProtoEntity = caosdb::entity::v1alpha1::Entity;
 using caosdb::entity::v1alpha1::EntityTransactionService;
+using caosdb::entity::v1alpha1::FileTransmissionService;
 using caosdb::entity::v1alpha1::IdResponse;
 using caosdb::entity::v1alpha1::MultiTransactionRequest;
 using caosdb::entity::v1alpha1::MultiTransactionResponse;
+using caosdb::entity::v1alpha1::RegisterFileUploadResponse;
 using caosdb::transaction::TransactionStatus;
 using WrappedResponseCase =
   caosdb::entity::v1alpha1::TransactionResponse::WrappedResponseCase;
@@ -219,6 +221,7 @@ private:
  */
 class Transaction {
 public:
+  auto RegisterUploadFile(RegisterFileUploadResponse *response) -> void;
   enum TransactionType {
     NONE,
     READ_ONLY,
@@ -228,7 +231,8 @@ public:
     MIXED_WRITE,
     MIXED_READ_AND_WRITE
   };
-  Transaction(std::shared_ptr<EntityTransactionService::Stub> service_stub);
+  Transaction(std::shared_ptr<EntityTransactionService::Stub> entity_service,
+              std::shared_ptr<FileTransmissionService::Stub> file_service);
 
   /**
    * Add an entity id to this transaction for retrieval.
@@ -339,7 +343,8 @@ private:
   TransactionType transaction_type = TransactionType::NONE;
   mutable std::unique_ptr<ResultSet> result_set;
   mutable TransactionStatus status = TransactionStatus::INITIAL();
-  std::shared_ptr<EntityTransactionService::Stub> service_stub;
+  std::shared_ptr<EntityTransactionService::Stub> entity_service;
+  std::shared_ptr<FileTransmissionService::Stub> file_service;
   MultiTransactionRequest *request;
   mutable MultiTransactionResponse *response;
   std::string error_message;
diff --git a/proto b/proto
index daf705e..e618844 160000
--- a/proto
+++ b/proto
@@ -1 +1 @@
-Subproject commit daf705ef4e86c30f96b3aac429a66667f50c7b77
+Subproject commit e6188445188a88c16c9a603060b40334e509ece6
diff --git a/src/caosdb/connection.cpp b/src/caosdb/connection.cpp
index 4ff50d2..b43444c 100644
--- a/src/caosdb/connection.cpp
+++ b/src/caosdb/connection.cpp
@@ -37,6 +37,7 @@ namespace caosdb::connection {
 using caosdb::configuration::ConfigurationManager;
 using caosdb::configuration::ConnectionConfiguration;
 using caosdb::entity::v1alpha1::EntityTransactionService;
+using caosdb::entity::v1alpha1::FileTransmissionService;
 using caosdb::info::VersionInfo;
 using caosdb::info::v1alpha1::GeneralInfoService;
 using caosdb::info::v1alpha1::GetVersionInfoRequest;
@@ -52,6 +53,8 @@ Connection::Connection(const ConnectionConfiguration &configuration) {
   this->general_info_service = GeneralInfoService::NewStub(this->channel);
   this->entity_transaction_service =
     std::make_shared<EntityTransactionService::Stub>(this->channel);
+  this->file_transmission_service =
+    std::make_shared<FileTransmissionService::Stub>(this->channel);
 }
 
 auto Connection::RetrieveVersionInfoNoExceptions() const noexcept
@@ -95,8 +98,9 @@ auto Connection::RetrieveVersionInfo() const -> const VersionInfo & {
 
 [[nodiscard]] auto Connection::CreateTransaction() const
   -> std::unique_ptr<Transaction> {
-  auto service_stub = this->entity_transaction_service;
-  return std::make_unique<Transaction>(service_stub);
+  auto entity_service = this->entity_transaction_service;
+  auto file_service = this->file_transmission_service;
+  return std::make_unique<Transaction>(entity_service, file_service);
 }
 
 auto ConnectionManager::mHasConnection(const std::string &name) const -> bool {
diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp
index ae9ddd2..3a23b49 100644
--- a/src/caosdb/transaction.cpp
+++ b/src/caosdb/transaction.cpp
@@ -91,8 +91,11 @@ auto get_status_description(int code) -> const std::string & {
 
 namespace caosdb::transaction {
 using caosdb::entity::v1alpha1::EntityTransactionService;
+using caosdb::entity::v1alpha1::FileTransmissionService;
 using caosdb::entity::v1alpha1::MultiTransactionRequest;
 using caosdb::entity::v1alpha1::MultiTransactionResponse;
+using caosdb::entity::v1alpha1::RegisterFileUploadRequest;
+using caosdb::entity::v1alpha1::RegisterFileUploadResponse;
 using WrappedResponseCase =
   caosdb::entity::v1alpha1::TransactionResponse::WrappedResponseCase;
 using caosdb::utility::get_arena;
@@ -106,12 +109,14 @@ using grpc::CompletionQueue;
 }
 
 Transaction::Transaction(
-  std::shared_ptr<EntityTransactionService::Stub> service_stub)
+  std::shared_ptr<EntityTransactionService::Stub> entity_service,
+  std::shared_ptr<FileTransmissionService::Stub> file_service)
   : request(google::protobuf::Arena::CreateMessage<MultiTransactionRequest>(
       get_arena())),
     response(google::protobuf::Arena::CreateMessage<MultiTransactionResponse>(
       get_arena())) {
-  this->service_stub = std::move(service_stub);
+  this->entity_service = std::move(entity_service);
+  this->file_service = std::move(file_service);
 }
 
 auto Transaction::RetrieveById(const std::string &id) noexcept -> StatusCode {
@@ -145,7 +150,7 @@ auto Transaction::InsertEntity(Entity *entity) noexcept -> StatusCode {
   ASSERT_CAN_ADD_INSERTION
 
   auto *sub_request = this->request->add_requests();
-  auto *proto_entity = sub_request->mutable_insert_request();
+  auto *proto_entity = sub_request->mutable_insert_request()->mutable_entity();
 
   // copy the original entity for the transaction
   entity->CopyTo(proto_entity);
@@ -156,10 +161,10 @@ 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);
+  auto *proto_entity = sub_request->mutable_update_request()->mutable_entity();
 
+  // copy the original entity for the transaction
+  entity->CopyTo(proto_entity);
   return StatusCode::INITIAL;
 }
 
@@ -197,8 +202,8 @@ auto Transaction::ExecuteAsynchronously() noexcept -> StatusCode {
 
   grpc::ClientContext context;
   std::unique_ptr<ClientAsyncResponseReader<MultiTransactionResponse>> rpc(
-    this->service_stub->PrepareAsyncMultiTransaction(&context, *(this->request),
-                                                     &cq));
+    this->entity_service->PrepareAsyncMultiTransaction(&context,
+                                                       *(this->request), &cq));
   rpc->StartCall();
 
   int tag = 1;
@@ -278,4 +283,29 @@ auto Transaction::WaitForIt() const noexcept -> TransactionStatus {
   return this->status;
 }
 
+auto Transaction::RegisterUploadFile(RegisterFileUploadResponse *response)
+  -> void {
+  grpc::Status grpc_status;
+  CompletionQueue cq;
+
+  RegisterFileUploadRequest request;
+
+  grpc::ClientContext context;
+  std::unique_ptr<ClientAsyncResponseReader<RegisterFileUploadResponse>> rpc(
+    this->file_service->PrepareAsyncRegisterFileUpload(&context, request, &cq));
+
+  rpc->StartCall();
+
+  int tag = 1;
+  void *send_tag = static_cast<void *>(&tag);
+  rpc->Finish(response, &grpc_status, send_tag);
+  void *recv_tag = nullptr;
+  bool ok = false;
+
+  // TODO(tf) make this actually asynchronous by moving this to WaitForIt()
+  cq.Next(&recv_tag, &ok);
+  assert(recv_tag == send_tag);
+  assert(ok);
+}
+
 } // namespace caosdb::transaction
diff --git a/test/test_entity.cpp b/test/test_entity.cpp
index 56d3c2a..412cce4 100644
--- a/test/test_entity.cpp
+++ b/test/test_entity.cpp
@@ -136,7 +136,8 @@ TEST(test_entity, test_copy_to) {
 
 TEST(test_entity, test_insert_entity) {
   auto transaction = caosdb::transaction::Transaction(
-    std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr));
+    std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr),
+    std::shared_ptr<transaction::FileTransmissionService::Stub>(nullptr));
 
   auto entity = Entity();
   entity.SetRole("entity_role");
@@ -154,7 +155,8 @@ TEST(test_entity, test_insert_entity) {
 // TODO(fspreck) cognitive complexity > 25 (threshold)
 TEST(test_entity, test_insert_with_role) { // NOLINT
   auto transaction = caosdb::transaction::Transaction(
-    std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr));
+    std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr),
+    std::shared_ptr<transaction::FileTransmissionService::Stub>(nullptr));
 
   auto entity = Entity();
   entity.SetRole("Property");
@@ -174,7 +176,8 @@ TEST(test_entity, test_insert_with_role) { // NOLINT
 
 TEST(test_entity, test_insert_with_parent) {
   auto transaction = caosdb::transaction::Transaction(
-    std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr));
+    std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr),
+    std::shared_ptr<transaction::FileTransmissionService::Stub>(nullptr));
 
   auto entity = Entity();
   entity.SetName("entity_name");
@@ -200,7 +203,8 @@ TEST(test_entity, test_insert_with_parent) {
 // 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));
+    std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr),
+    std::shared_ptr<transaction::FileTransmissionService::Stub>(nullptr));
 
   auto entity = Entity();
   entity.SetName("entity_name");
-- 
GitLab