From 28e440ba8ccf94c9b994f8c3ba99ff0fb1c597ef Mon Sep 17 00:00:00 2001
From: Timm Fitschen <t.fitschen@indiscale.com>
Date: Tue, 31 Aug 2021 16:27:48 +0200
Subject: [PATCH] BUG: remove FixValue, fix linting errors, move and complete
 get_status_description

---
 CMakeLists.txt                         |   2 +-
 Makefile                               |   4 +-
 include/caosdb/entity.h                |  20 +--
 include/caosdb/status_code.h           |  24 +++-
 include/caosdb/utility.h               |  42 +++---
 include/caosdb/value.h                 |   1 -
 include/ccaosdb.h                      |  24 ++--
 src/CMakeLists.txt                     |   1 +
 src/caosdb/entity.cpp                  | 123 +-----------------
 src/caosdb/status_code_description.cpp | 170 +++++++++++++++++++++++++
 src/caosdb/transaction.cpp             |  66 ----------
 src/caosdb/utility.cpp                 |   7 +-
 test/test_ccaosdb.cpp                  |   3 +-
 test/test_data_type.cpp                |   4 +
 test/test_entity.cpp                   |  23 ++--
 test/test_issues.cpp                   |  17 +--
 test/test_utility.cpp                  |   6 +-
 17 files changed, 265 insertions(+), 272 deletions(-)
 create mode 100644 src/caosdb/status_code_description.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ec6d2ae..80a1396 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -415,7 +415,7 @@ option(AUTOFORMATTING "call clang-format at configure time" ON)
 if(AUTOFORMATTING AND NOT SKIP_LINTING)
     find_program(clang_format NAMES clang-format-11 clang-format)
     file(GLOB format_test_sources test/*.cpp test/*.h test/*.h.in)
-    execute_process(COMMAND $(clang-format) -i --verbose ${libcaosdb_INCL}
+    execute_process(COMMAND ${clang_format} -i --verbose ${libcaosdb_INCL}
         ${libcaosdb_SRC} ${libcaosdb_TEST_SRC}
         ${PROJECT_SOURCE_DIR}/src/cxxcaosdbcli.cpp
         ${PROJECT_SOURCE_DIR}/src/ccaosdbcli.c
diff --git a/Makefile b/Makefile
index 84a0765..a024d31 100644
--- a/Makefile
+++ b/Makefile
@@ -22,7 +22,7 @@
 # This Makefile is a wrapper for several other scripts.
 
 
-CLANG-FORMAT = clang-format-11
+CLANG_FORMAT ?= clang-format-11
 
 .PHONY: help
 help:
@@ -31,7 +31,7 @@ help:
 	@echo "    style - auto-format the source files."
 
 style:
-	$(CLANG-FORMAT) -i --verbose \
+	$(CLANG_FORMAT) -i --verbose \
 	 $$(find test/ src/ include/ -type f -iname "*.cpp" -o -iname "*.h" -o -iname "*.h.in")
 .PHONY: style
 
diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h
index f5b5a59..3a8f1f9 100644
--- a/include/caosdb/entity.h
+++ b/include/caosdb/entity.h
@@ -454,9 +454,7 @@ class Property {
 public:
   explicit inline Property(ProtoProperty *other)
     : value(Value(other->mutable_value())), data_type(DataType(other->mutable_data_type())),
-      wrapped(other) {
-    FixValue();
-  };
+      wrapped(other){};
   Property();
 
   /**
@@ -553,12 +551,6 @@ public:
   friend class RepeatedPtrFieldWrapper<Property, ProtoProperty>;
 
 private:
-  /**
-   * Workaround until non-string values are supported by the server.
-   *
-   * Only has an effect if there is a DataType.
-   */
-  auto FixValue() -> void;
   static auto CreateProtoProperty() -> ProtoProperty *;
   Value value;
   DataType data_type;
@@ -610,7 +602,6 @@ public:
     errors.wrapped = CreateMessagesField();
     warnings.wrapped = CreateMessagesField();
     infos.wrapped = CreateMessagesField();
-    FixValue();
   };
   explicit Entity(IdResponse *id_response);
   explicit Entity(ProtoEntity *other)
@@ -623,13 +614,11 @@ public:
     errors.wrapped = CreateMessagesField();
     warnings.wrapped = CreateMessagesField();
     infos.wrapped = CreateMessagesField();
-    FixValue();
   };
   explicit inline Entity(EntityResponse *response) : Entity(response->release_entity()) {
     errors.wrapped->Swap(response->mutable_errors());
     warnings.wrapped->Swap(response->mutable_warnings());
     infos.wrapped->Swap(response->mutable_infos());
-    FixValue();
   };
 
   [[nodiscard]] inline auto GetId() const noexcept -> const std::string & { return wrapped->id(); };
@@ -755,13 +744,6 @@ private:
   auto SetId(const std::string &id) -> void;
   auto SetVersionId(const std::string &id) -> void;
 
-  /**
-   * Workaround until non-string values are supported by the server.
-   *
-   * Only has an effect if there is a DataType.
-   */
-  auto FixValue() -> void;
-
 private:
   FileDescriptor file_descriptor;
   ProtoEntity *wrapped;
diff --git a/include/caosdb/status_code.h b/include/caosdb/status_code.h
index 5689b9e..8a21ea3 100644
--- a/include/caosdb/status_code.h
+++ b/include/caosdb/status_code.h
@@ -22,7 +22,8 @@
 #ifndef CAOSDB_STATUS_CODE_H
 #define CAOSDB_STATUS_CODE_H
 
-#include <string>
+#include <grpcpp/impl/codegen/status_code_enum.h> // for StatusCode
+#include <string>                                 // for string
 
 namespace caosdb {
 
@@ -40,10 +41,23 @@ enum StatusCode {
   GO_ON = -3,
   INITIAL = -2,
   EXECUTING = -1,
-  SUCCESS = 0,
-  // TODO(tf) Map other GRPC errors
-  AUTHENTICATION_ERROR = 16,
-  CONNECTION_ERROR = 14,
+  SUCCESS = grpc::StatusCode::OK,                              // = 0
+  CANCELLED = grpc::StatusCode::CANCELLED,                     // = 1
+  UNKNOWN = grpc::StatusCode::UNKNOWN,                         // = 2
+  INVALID_ARGUMENT = grpc::StatusCode::INVALID_ARGUMENT,       // = 3
+  DEADLINE_EXCEEDED = grpc::StatusCode::DEADLINE_EXCEEDED,     // = 4
+  NOT_FOUND = grpc::StatusCode::NOT_FOUND,                     // = 5
+  ALREADY_EXISTS = grpc::StatusCode::ALREADY_EXISTS,           // = 6
+  PERMISSION_DENIED = grpc::StatusCode::PERMISSION_DENIED,     // = 7
+  RESOURCE_EXHAUSTED = grpc::StatusCode::RESOURCE_EXHAUSTED,   // = 8
+  FAILED_PRECONDITION = grpc::StatusCode::FAILED_PRECONDITION, // = 9
+  ABORTED = grpc::StatusCode::ABORTED,                         // = 10
+  OUT_OF_RANGE = grpc::StatusCode::OUT_OF_RANGE,               // = 11
+  UNIMPLEMENTED = grpc::StatusCode::UNIMPLEMENTED,             // = 12
+  INTERNAL = grpc::StatusCode::INTERNAL,                       // = 13
+  CONNECTION_ERROR = grpc::StatusCode::UNAVAILABLE,            // = 14
+  DATA_LOSS = grpc::StatusCode::DATA_LOSS,                     // = 15
+  AUTHENTICATION_ERROR = grpc::StatusCode::UNAUTHENTICATED,    // = 16
   GENERIC_RPC_ERROR = 20,
   GENERIC_ERROR = 21,
   GENERIC_TRANSACTION_ERROR = 22,
diff --git a/include/caosdb/utility.h b/include/caosdb/utility.h
index bfe3780..5af1b49 100644
--- a/include/caosdb/utility.h
+++ b/include/caosdb/utility.h
@@ -21,25 +21,25 @@
 
 #ifndef CAOSDB_UTILS_H
 #define CAOSDB_UTILS_H
-#include "caosdb/entity.h"
-#include "caosdb/data_type.h"
-#include <boost/beast/core/detail/base64.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/fstream.hpp>
-#include <boost/filesystem/string_file.hpp>
-#include <boost/lexical_cast.hpp> // for lexical_cast
-#include <boost/json.hpp>
-#include <cassert>
-#include <cstdlib>
-#include <exception> // for logic_error
-#include <fstream>
-#include <iostream>
-#include <map>
-#include <memory>
-#include <mutex>
-#include <shared_mutex>
-#include <string>
-#include <string_view>
+#include "caosdb/data_type.h"                 // for AtomicDataType
+#include "caosdb/entity.h"                    // for Importance, Role
+#include <boost/beast/core/detail/base64.hpp> // for encoded_size
+#include <boost/beast/core/detail/base64.ipp> // for encode
+#include <boost/filesystem/operations.hpp>    // for exists
+#include <boost/filesystem/path.hpp>          // for path
+#include <boost/filesystem/fstream.hpp>       // for basic_ifstream, ifstream
+#include <boost/filesystem/string_file.hpp>   // for load_string_file
+#include <boost/json/stream_parser.hpp>       // for stream_parser
+#include <boost/json/value.hpp>               // for value
+#include <boost/lexical_cast.hpp>             // for lexical_cast
+#include <cassert>                            // for assert
+#include <cstdlib>                            // for getenv
+#include <fstream>                            // for basic_istream<>::__ist...
+#include <memory>                             // for allocator, unique_ptr
+#include <stdexcept>                          // for logic_error
+#include <string>                             // for string, operator+, cha...
+#include <type_traits>                        // for underlying_type_t
+#include <typeinfo>                           // for type_info
 
 namespace caosdb::utility {
 using boost::filesystem::exists;
@@ -71,9 +71,7 @@ template <> auto getEnumNameFromValue<caosdb::entity::Role>(caosdb::entity::Role
  *
  * @detail May be useful for higher-order CaosDB clients and only makes sense if specialized.
  */
-template <typename Enum> auto getEnumValueFromName(const std::string &name) -> Enum {
-  throw std::logic_error(std::string("Enum type ") + typeid(Enum).name() + " not implemented.");
-}
+template <typename Enum> auto getEnumValueFromName(const std::string &name) -> Enum;
 
 // Forward declaration of specializations
 template <>
diff --git a/include/caosdb/value.h b/include/caosdb/value.h
index 989f780..2ec817e 100644
--- a/include/caosdb/value.h
+++ b/include/caosdb/value.h
@@ -197,7 +197,6 @@ public:
 
   inline auto ToString() const noexcept -> const std::string {
     CAOSDB_DEBUG_MESSAGE_STRING(*wrapped, out)
-    CAOSDB_LOG_DEBUG("caosdb::entity") << "HERE 1 [" << wrapped << "] " << out;
     return out;
   }
 
diff --git a/include/ccaosdb.h b/include/ccaosdb.h
index cfe5cf4..38b5d5e 100644
--- a/include/ccaosdb.h
+++ b/include/ccaosdb.h
@@ -68,7 +68,7 @@ int caosdb_status_code_OTHER_CLIENT_ERROR();
  * capability for type checking in C even though the C++ class
  * Connection is opaque in C.
  */
-typedef struct {
+typedef struct caosdb_connection_connection {
   void *wrapped_connection;
   bool _deletable = false;
 } caosdb_connection_connection;
@@ -80,7 +80,7 @@ typedef struct {
  * capability for type checking in C even though the C++ class
  * Connection is opaque in C.
  */
-typedef struct {
+typedef struct caosdb_connection_connection_configuration {
   void *wrapped_connection_configuration;
   bool _deletable = false;
 } caosdb_connection_connection_configuration;
@@ -92,7 +92,7 @@ typedef struct {
  * capability for type checking in C even though the C++ class
  * Connection is opaque in C.
  */
-typedef struct {
+typedef struct caosdb_info_version_info {
   int major;
   int minor;
   int patch;
@@ -100,12 +100,12 @@ typedef struct {
   const char *build;
 } caosdb_info_version_info;
 
-typedef struct {
+typedef struct caosdb_connection_certificate_provider {
   void *wrapped_certificate_provider;
   bool _deletable = false;
 } caosdb_connection_certificate_provider;
 
-typedef struct {
+typedef struct caosdb_authentication_authenticator {
   void *wrapped_authenticator;
   bool _deletable = false;
 } caosdb_authentication_authenticator;
@@ -266,7 +266,7 @@ int caosdb_connection_connection_manager_get_connection(caosdb_connection_connec
 
 // TODO(fspreck) implementations needed, and probably these declarations are
 // not sufficient yet.
-typedef struct {
+typedef struct caosdb_transaction_transaction {
   void *wrapped_transaction;
   bool _deletable = false;
 } caosdb_transaction_transaction;
@@ -292,7 +292,7 @@ int caosdb_transaction_transaction_execute(caosdb_transaction_transaction *trans
 // TODO(fspreck) execute_asynchronously may be added as a separate
 // function once we actually support asynchronous execution.
 
-typedef struct {
+typedef struct caosdb_transaction_result_set {
   void *wrapped_result_set;
   bool _deletable = false;
 } caosdb_transaction_result_set;
@@ -303,7 +303,7 @@ int caosdb_transaction_transaction_get_result_set(caosdb_transaction_transaction
 int caosdb_transaction_transaction_get_count_result(caosdb_transaction_transaction *transaction,
                                                     long *out);
 
-typedef struct {
+typedef struct caosdb_entity_entity {
   void *wrapped_entity;
   bool _deletable = false;
 } caosdb_entity_entity;
@@ -319,15 +319,17 @@ int caosdb_transaction_transaction_update_entity(caosdb_transaction_transaction
 int caosdb_transaction_transaction_delete_by_id(caosdb_transaction_transaction *transaction,
                                                 const char *id);
 
-typedef struct {
+typedef struct caosdb_entity_property {
   void *wrapped_property;
   bool _deletable = false;
 } caosdb_entity_property;
-typedef struct {
+
+typedef struct caosdb_entity_parent {
   void *wrapped_parent;
   bool _deletable = false;
 } caosdb_entity_parent;
-typedef struct {
+
+typedef struct caosdb_entity_message {
   void *wrapped_message;
   bool _deletable = false;
 } caosdb_entity_message;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cb00898..a3654e0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -36,6 +36,7 @@ set(libcaosdb_SRC
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/download_request_handler.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/file_writer.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/file_reader.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/status_code_description.cpp
     )
 
 # pass variable to parent scope
diff --git a/src/caosdb/entity.cpp b/src/caosdb/entity.cpp
index ed7ede3..18c9cb5 100644
--- a/src/caosdb/entity.cpp
+++ b/src/caosdb/entity.cpp
@@ -20,14 +20,10 @@
  *
  */
 #include "caosdb/entity.h"
-#include "caosdb/data_type.h"               // for DataType
-#include "caosdb/entity/v1alpha1/main.pb.h" // for Messages
-#include "caosdb/exceptions.h"
-#include "caosdb/logging.h"         // for CAOSDB_LOG_TRACE
-#include "caosdb/protobuf_helper.h" // for get_arena
-#include "caosdb/utility.h"
-#include "caosdb/value.h" // for Value
-#include <boost/algorithm/string.hpp>
+#include "caosdb/data_type.h"                       // for DataType
+#include "caosdb/entity/v1alpha1/main.pb.h"         // for Messages
+#include "caosdb/protobuf_helper.h"                 // for get_arena
+#include "caosdb/value.h"                           // for Value
 #include <google/protobuf/arena.h>                  // for Arena
 #include <google/protobuf/generated_message_util.h> // for Arena::Create...
 #include <new>                                      // for operator new
@@ -46,10 +42,6 @@ using google::protobuf::Arena;
 
 Messages::~Messages() = default;
 
-// Forward declarations ///////////////////////////////////////////////////////
-
-template <typename E> auto FixValueImpl(E *ent) -> void;
-
 // Parent /////////////////////////////////////////////////////////////////////
 
 Parent::Parent() : wrapped(Parent::CreateProtoParent()) {
@@ -76,7 +68,7 @@ auto Parent::SetId(const std::string &id) -> void { this->wrapped->set_id(id); }
   return this->wrapped->description();
 }
 
-Property::Property() : Property(Property::CreateProtoProperty()) { FixValue(); }
+Property::Property() : Property(Property::CreateProtoProperty()) {}
 
 auto Property::CreateProtoProperty() -> ProtoProperty * {
   return Arena::CreateMessage<ProtoProperty>(get_arena());
@@ -162,8 +154,6 @@ auto Property::SetDataType(const std::string &new_data_type, bool list_type) ->
   return SetDataType(DataType(new_data_type, list_type));
 }
 
-auto Property::FixValue() -> void { FixValueImpl(this); }
-
 // Entity /////////////////////////////////////////////////////////////////////
 [[nodiscard]] auto Entity::GetParents() const -> const Parents & { return parents; }
 
@@ -187,10 +177,9 @@ Entity::Entity(IdResponse *id_response) : Entity() {
   this->errors.wrapped->Swap(id_response->mutable_errors());
   this->warnings.wrapped->Swap(id_response->mutable_warnings());
   this->infos.wrapped->Swap(id_response->mutable_infos());
-  FixValue();
 }
 
-Entity::Entity() : Entity(Entity::CreateProtoEntity()) { FixValue(); }
+Entity::Entity() : Entity(Entity::CreateProtoEntity()) {}
 
 auto Entity::CreateMessagesField() -> RepeatedPtrField<ProtoMessage> * {
   return Arena::CreateMessage<RepeatedPtrField<ProtoMessage>>(get_arena());
@@ -270,104 +259,4 @@ auto Entity::SetFilePath(const std::string &path) -> void {
   this->wrapped->mutable_file_descriptor()->set_path(path);
 }
 
-auto Entity::FixValue() -> void { FixValueImpl(this); }
-
-// Utility functions //////////////////////////////////////////////////////////
-
-// TODO(daniel) cognitive complexity is too high
-template <typename E> auto FixValueImpl(E *ent) -> void { // NOLINT
-  const auto &dtype = ent->GetDataType();
-  const auto &value = ent->GetValue();
-  auto new_value = Value();
-  CAOSDB_LOG_TRACE(logger_name) << "FixValueImpl()\n" << value.ToString();
-  if (value.IsNull() || !value.IsString()) { // Don't treat NULL and non-string values.
-    return;
-  }
-  if (value.IsList()) { // Also don't treat empty or non-string lists.
-    const auto &list = value.AsList();
-    if (list.empty() || !list[0].IsString()) {
-      return;
-    }
-  }
-  auto atype = AtomicDataType::UNSPECIFIED;
-  if (dtype.IsList()) { // List Datatype
-    if (!value.IsList()) {
-      throw caosdb::exceptions::Exception(StatusCode::OTHER_CLIENT_ERROR,
-                                          "DataType is list, but Value is scalar.");
-    }
-    auto &list_type = dtype.AsList();
-    atype = list_type.GetAtomicDataType();
-    if (!list_type.IsListOfAtomic() // References, strings etc. need no treatment.
-        || atype == AtomicDataType::UNSPECIFIED || atype == AtomicDataType::TEXT ||
-        atype == AtomicDataType::DATETIME) {
-      return;
-    }
-    if (atype == AtomicDataType::DOUBLE) {
-      std::vector<double> data;
-      for (auto &d : value.AsList()) {
-        data.push_back(std::stod(d.AsString()));
-      }
-      new_value = Value(data);
-    } else if (atype == AtomicDataType::INTEGER) {
-      std::vector<int32_t> data;
-      for (auto &d : value.AsList()) {
-        data.push_back(std::stoi(d.AsString()));
-      }
-      new_value = Value(data);
-    } else if (atype == AtomicDataType::BOOLEAN) {
-      std::vector<bool> data;
-      for (auto &d : value.AsList()) {
-        const auto &bool_value = d.AsString();
-        if (boost::to_upper_copy(bool_value) == "TRUE") {
-          data.push_back(true);
-        } else if (boost::to_upper_copy(bool_value) == "FALSE") {
-          data.push_back(false);
-        } else {
-          throw caosdb::exceptions::Exception(StatusCode::OTHER_CLIENT_ERROR,
-                                              "Boolean value is neither true nor false.");
-        }
-      }
-      new_value = Value(data);
-    } else {
-      std::cout << "Unhandled datatype: " << ent->ToString() << std::endl;
-      throw std::logic_error("Unhandled datatype");
-    }
-  } else { // Scalar Datatype
-    if (value.IsList()) {
-      throw caosdb::exceptions::Exception(StatusCode::OTHER_CLIENT_ERROR,
-                                          "Value is list, but DataType is scalar.");
-    }
-    atype = dtype.AsAtomic();
-    if (!dtype.IsAtomic() // References, strings etc. need no treatment.
-        || atype == AtomicDataType::UNSPECIFIED || atype == AtomicDataType::TEXT ||
-        atype == AtomicDataType::DATETIME) {
-      return;
-    }
-    CAOSDB_LOG_TRACE(logger_name) << "AtomicDataType: "
-                                  << caosdb::utility::getEnumNameFromValue(atype);
-    if (atype == AtomicDataType::DOUBLE) {
-      CAOSDB_LOG_TRACE(logger_name) << "double: " << value.AsString();
-      new_value = Value(std::strtod(value.AsString().c_str(), nullptr));
-      CAOSDB_LOG_TRACE(logger_name) << "   ->   " << new_value.AsDouble();
-    } else if (atype == AtomicDataType::INTEGER) {
-      new_value = Value(std::stol(value.AsString()));
-    } else if (atype == AtomicDataType::BOOLEAN) {
-      const auto &bool_value = value.AsString();
-      if (boost::to_upper_copy(bool_value) == "TRUE") {
-        new_value = Value(true);
-      } else if (boost::to_upper_copy(bool_value) == "FALSE") {
-        new_value = Value(false);
-      } else {
-        throw caosdb::exceptions::Exception(StatusCode::OTHER_CLIENT_ERROR,
-                                            "Boolean value is neither true nor false.");
-      }
-    } else {
-      std::cout << "Unhandled datatype: " << ent->ToString() << std::endl;
-      throw std::logic_error("Unhandled datatype");
-    }
-  }
-
-  ent->SetValue(new_value);
-}
-
 } // namespace caosdb::entity
diff --git a/src/caosdb/status_code_description.cpp b/src/caosdb/status_code_description.cpp
new file mode 100644
index 0000000..72e11e0
--- /dev/null
+++ b/src/caosdb/status_code_description.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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/status_code.h" // for StatusCode, ABORTED, ALREADY_EXISTS
+#include <map>                  // for allocator, map
+#include <stdexcept>            // for out_of_range
+#include <string>               // for string, basic_string
+
+namespace caosdb {
+
+/*
+ * The descriptions of the StatusCodes 1-16 are originally taken from
+ * https://github.com/grpc/grpc/blob/ce5b4e949fc75ed4c19e9ccfec7dc95c8ee9ae45/doc/statuscodes.md
+ * and adapted to our purposes.
+ *
+ * They are origially released under an Apache-2.0 license in the linked
+ * repository. However, the linked file itself does not contain a copyright or
+ * license statement and so we have to assume that the copyright holders are an
+ * unknown subset of the contributers of that repository:
+ *
+ * Copyright (C) 2015-2021 unknown gRPC Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+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 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::CANCELLED, "The operation was cancelled (typically by the caller)."},
+    {StatusCode::UNKNOWN, "Unknown error. This is typically a bug (server or client)."},
+    {StatusCode::INVALID_ARGUMENT,
+     "Client specified an invalid argument. Note that this differs from FAILED_PRECONDITION. "
+     "INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the "
+     "system (e.g., a malformed file name)."},
+    {StatusCode::DEADLINE_EXCEEDED,
+     "Deadline expired before operation could complete. For operations that change the state of "
+     "the system, this error may be returned even if the operation has completed successfully. For "
+     "example, a successful response from a server could have been delayed long enough for the "
+     "deadline to expire."},
+    {StatusCode::NOT_FOUND, "Some requested entity (e.g., file or directory) was not found."},
+    {StatusCode::ALREADY_EXISTS,
+     "Some entity that we attempted to create (e.g., file or directory) already exists."},
+    {StatusCode::PERMISSION_DENIED,
+     "The caller does not have permission to execute the specified operation. PERMISSION_DENIED "
+     "must not be used for rejections caused by exhausting some resource (use RESOURCE_EXHAUSTED "
+     "instead for those errors). PERMISSION_DENIED must not be used if the caller can not be "
+     "identified (use UNAUTHENTICATED instead for those errors)."},
+    {StatusCode::RESOURCE_EXHAUSTED, "Some resource has been exhausted, perhaps a per-user quota, "
+                                     "or perhaps the entire file system is out of space."},
+    {StatusCode::FAILED_PRECONDITION,
+     "Operation was rejected because the system is not in a state required for the operation's "
+     "execution. For example, directory to be deleted may be non-empty, an rmdir operation is "
+     "applied to a non-directory, etc. A litmus test that may help a service implementor in "
+     "deciding between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: (a) Use UNAVAILABLE if the "
+     "client can retry just the failing call. (b) Use ABORTED if the client should retry at a "
+     "higher-level (e.g., restarting a read-modify-write sequence). (c) Use FAILED_PRECONDITION if "
+     "the client should not retry until the system state has been explicitly fixed. E.g., if an "
+     "'rmdir' fails because the directory is non-empty, FAILED_PRECONDITION should be returned "
+     "since the client should not retry unless they have first fixed up the directory by deleting "
+     "files from it. (d) Use FAILED_PRECONDITION if the client performs conditional REST "
+     "Get/Update/Delete on a resource and the resource on the server does not match the condition. "
+     "E.g., conflicting read-modify-write on the same resource."},
+    {StatusCode::ABORTED,
+     "The operation was aborted, typically due to a concurrency issue like sequencer check "
+     "failures, transaction aborts, etc. See litmus test above for deciding between "
+     "FAILED_PRECONDITION, ABORTED, and UNAVAILABLE."},
+    {StatusCode::OUT_OF_RANGE,
+     "Operation was attempted past the valid range. E.g., seeking or reading past end of file. "
+     "Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed if the system "
+     "state changes. For example, a 32-bit file system will generate INVALID_ARGUMENT if asked to "
+     "read at an offset that is not in the range [0,2^32-1], but it will generate OUT_OF_RANGE if "
+     "asked to read from an offset past the current file size. There is a fair bit of overlap "
+     "between FAILED_PRECONDITION and OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more "
+     "specific error) when it applies so that callers who are iterating through a space can easily "
+     "look for an OUT_OF_RANGE error to detect when they are done."},
+    {StatusCode::UNIMPLEMENTED,
+     "Operation is not implemented or not supported/enabled in this service. In contrast to "
+     "UNSUPPORTED_FEATURE (which is a client error) this error indicates that the server does not "
+     "support this operation."},
+    {StatusCode::INTERNAL,
+     "Internal errors. Means some invariants expected by underlying System has been broken. If you "
+     "see one of these errors, Something is very broken. "},
+    {StatusCode::DATA_LOSS, "Unrecoverable data loss or corruption."},
+
+    {StatusCode::CONNECTION_ERROR,
+     "The attempt to execute this transaction was not successful because the "
+     "connection to the server could not be established."},
+    {StatusCode::AUTHENTICATION_ERROR,
+     "The attempt to execute this transaction has not been executed at all "
+     "because the authentication did not succeed."},
+    {StatusCode::GENERIC_RPC_ERROR,
+     "The attempt to execute this transaction was not successful because an "
+     "error occured in the transport or RPC protocol layer."},
+    {StatusCode::GENERIC_ERROR, "An error occured. Please review the logs for more information."},
+    {StatusCode::GENERIC_TRANSACTION_ERROR,
+     "The transaction terminated unsuccessfully with transaction errors."},
+    {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."},
+    {StatusCode::TRANSACTION_STATUS_ERROR,
+     "The Transaction is in a wrong state for the attempted action."},
+    {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 not "
+     "have "
+     "an id. This is the case when you did not retrieve it before applying any "
+     "changes and instantiated the Entity class explicitely."},
+    {StatusCode::NOT_A_FILE_ENTITY, "You can only add files to file entities."},
+    {StatusCode::PATH_IS_A_DIRECTORY, "The given path is a directory."},
+    {StatusCode::FILE_DOES_NOT_EXIST_LOCALLY,
+     "The file does not not exist in the local file system."},
+    {StatusCode::FILE_DOWNLOAD_ERROR, "The transaction failed during the download of the files"},
+    {StatusCode::FILE_UPLOAD_ERROR, "The transaction failed during the upload of the files"},
+    {StatusCode::UNSUPPORTED_FEATURE,
+     "This feature is not available in the this client implementation."},
+    {StatusCode::EXTERN_C_ASSIGNMENT_ERROR,
+     "You tried to assign a new object to the wrapped void pointer. You have "
+     "to delete the old pointee first."},
+    {StatusCode::ENUM_MAPPING_ERROR,
+     "The role, importance, or datatype you specified does not exist."},
+    {StatusCode::OTHER_CLIENT_ERROR,
+     "This is code is reserved to errors raised by other clients wrapping the "
+     "C++ client (or its Extern C interface).  This should never occur when "
+     "working with the C++ code itself."}};
+  try {
+    return descriptions.at(code);
+  } catch (const std::out_of_range &exc) {
+    return MISSING_DESCRIPTION;
+  }
+}
+
+} // namespace caosdb
diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp
index 58bf54e..551d905 100644
--- a/src/caosdb/transaction.cpp
+++ b/src/caosdb/transaction.cpp
@@ -43,74 +43,8 @@
 #include <iosfwd>                                   // for streamsize
 #include <map>                                      // for map, operator!=
 #include <memory>                                   // for unique_ptr
-#include <stdexcept>                                // for out_of_range
 #include <utility>                                  // for move, pair
 
-namespace caosdb {
-
-// TODO(tf) move to another source file.
-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 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,
-     "The attempt to execute this transaction was not successful because the "
-     "connection to the server could not be established."},
-    {StatusCode::AUTHENTICATION_ERROR,
-     "The attempt to execute this transaction has not been executed at all "
-     "because the authentication did not succeed."},
-    {StatusCode::GENERIC_RPC_ERROR,
-     "The attempt to execute this transaction was not successful because an "
-     "error occured in the transport or RPC protocol layer."},
-    {StatusCode::GENERIC_ERROR, "An error occured. Please review the logs for more information."},
-    {StatusCode::GENERIC_TRANSACTION_ERROR,
-     "The transaction terminated unsuccessfully with transaction errors."},
-    {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."},
-    {StatusCode::TRANSACTION_STATUS_ERROR,
-     "The Transaction is in a wrong state for the attempted action."},
-    {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 not "
-     "have "
-     "an id. This is the case when you did not retrieve it before applying any "
-     "changes and instantiated the Entity class explicitely."},
-    {StatusCode::NOT_A_FILE_ENTITY, "You can only add files to file entities."},
-    {StatusCode::PATH_IS_A_DIRECTORY, "The given path is a directory."},
-    {StatusCode::FILE_DOES_NOT_EXIST_LOCALLY,
-     "The file does not not exist in the local file system."},
-    {StatusCode::FILE_DOWNLOAD_ERROR, "The transaction failed during the download of the files"},
-    {StatusCode::FILE_UPLOAD_ERROR, "The transaction failed during the upload of the files"},
-    {StatusCode::UNSUPPORTED_FEATURE,
-     "This feature is not available in the this client implementation."},
-    {StatusCode::EXTERN_C_ASSIGNMENT_ERROR,
-     "You tried to assign a new object to the wrapped void pointer. You have "
-     "to delete the old pointee first."},
-    {StatusCode::ENUM_MAPPING_ERROR,
-     "The role, importance, or datatype you specified does not exist."},
-    {StatusCode::OTHER_CLIENT_ERROR,
-     "This is code is reserved to errors raised by other clients wrapping the "
-     "C++ client (or its Extern C interface).  This should never occur when "
-     "working with the C++ code itself."}};
-  try {
-    return descriptions.at(code);
-  } catch (const std::out_of_range &exc) {
-    return MISSING_DESCRIPTION;
-  }
-}
-
-} // namespace caosdb
-
 namespace caosdb::transaction {
 using caosdb::entity::v1alpha1::EntityTransactionService;
 using caosdb::entity::v1alpha1::FileTransmissionService;
diff --git a/src/caosdb/utility.cpp b/src/caosdb/utility.cpp
index d049b91..a4ed293 100644
--- a/src/caosdb/utility.cpp
+++ b/src/caosdb/utility.cpp
@@ -18,10 +18,11 @@
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
  *
  */
-#include "caosdb/data_type.h"
-#include "caosdb/entity.h"
 #include "caosdb/utility.h"
-#include <algorithm>
+#include "caosdb/data_type.h" // for AtomicDataType, atomicdatatype_names
+#include "caosdb/entity.h"    // for Importance, Role, importance_names
+#include <map>                // for map, operator!=, _Rb_tree_const_iterator
+#include <utility>            // for pair
 
 namespace caosdb::utility {
 
diff --git a/test/test_ccaosdb.cpp b/test/test_ccaosdb.cpp
index 475a5d1..90e72e5 100644
--- a/test/test_ccaosdb.cpp
+++ b/test/test_ccaosdb.cpp
@@ -28,8 +28,7 @@
 #include <gtest/gtest-message.h>   // for Message
 #include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl
 #include <gtest/gtest_pred_impl.h> // for Test, TestInfo, EXPECT_EQ, TEST
-#include <iostream>
-#include <string> // for allocator
+#include <string>                  // for allocator, operator+, string,...
 
 class test_ccaosdb : public ::testing::Test {
 protected:
diff --git a/test/test_data_type.cpp b/test/test_data_type.cpp
index 9200a68..bb2e97b 100644
--- a/test/test_data_type.cpp
+++ b/test/test_data_type.cpp
@@ -22,7 +22,9 @@
 
 #include "caosdb/data_type.h"                          // for DataType, AtomicDataType
 #include "caosdb/entity.h"                             // for Entity
+#include "caosdb/entity/v1alpha1/main.pb.h"            // for DataType, Ato...
 #include "caosdb/logging.h"                            // for CAOSDB_LOG_DEBUG
+#include "caosdb/protobuf_helper.h"                    // for CAOSDB_DEBUG_...
 #include <boost/log/core/record.hpp>                   // for record
 #include <boost/log/detail/attachable_sstream_buf.hpp> // for basic_ostring...
 #include <boost/log/sources/record_ostream.hpp>        // for operator<<
@@ -32,7 +34,9 @@
 #include <gtest/gtest-test-part.h>                     // for TestPartResult, SuiteApi...
 #include <gtest/gtest_pred_impl.h>                     // for AssertionResult, Test
 #include <iosfwd>                                      // for streamsize
+#include <map>                                         // for map, operator!=
 #include <string>                                      // for allocator
+#include <utility>                                     // for pair
 
 namespace caosdb::entity {
 using ProtoEntity = caosdb::entity::v1alpha1::Entity;
diff --git a/test/test_entity.cpp b/test/test_entity.cpp
index 95c451d..df196c1 100644
--- a/test/test_entity.cpp
+++ b/test/test_entity.cpp
@@ -20,25 +20,24 @@
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
  *
  */
-#include "caosdb_test_utility.h"
 #include "caosdb/data_type.h"                    // for DataType, AtomicDat...
-#include "caosdb/entity.h"                       // for Entity, Parent, Par...
+#include "caosdb/entity.h"                       // for Entity, Property
 #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/message_code.h"                 // for MessageCode, ENTITY...
 #include "caosdb/protobuf_helper.h"              // for get_arena
 #include "caosdb/status_code.h"                  // for StatusCode, FILE_DO...
 #include "caosdb/transaction.h"                  // for Transaction
 #include "caosdb/value.h"                        // for Value
-#include <exception>
-#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
-#include <iostream>
-#include <memory> // for allocator, shared_ptr
-#include <stdexcept>
-#include <string> // for operator+, string
+#include "caosdb_test_utility.h"                 // for TEST_DATA_DIR
+#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
+#include <iostream>                              // for operator<<, basic_o...
+#include <memory>                                // for allocator, shared_ptr
+#include <stdexcept>                             // for out_of_range
+#include <string>                                // for operator+, to_string
 
 namespace caosdb::entity {
 using caosdb::entity::v1alpha1::IdResponse;
diff --git a/test/test_issues.cpp b/test/test_issues.cpp
index 2b414a7..389c8ee 100644
--- a/test/test_issues.cpp
+++ b/test/test_issues.cpp
@@ -17,14 +17,15 @@
  * 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/configuration.h" // for InsecureConnectionConfig...
-#include "caosdb/connection.h"    // for Connection
-#include "caosdb/exceptions.h"    // for ConnectionError
-#include "caosdb/status_code.h"
-#include "caosdb/transaction.h"    // for Transaction
-#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 "caosdb/configuration.h"      // for InsecureConnectionConfig...
+#include "caosdb/connection.h"         // for Connection
+#include "caosdb/status_code.h"        // for StatusCode, EXECUTING
+#include "caosdb/transaction.h"        // for Transaction
+#include "caosdb/transaction_status.h" // for StatusCode
+#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;
diff --git a/test/test_utility.cpp b/test/test_utility.cpp
index a64fcee..875cfbf 100644
--- a/test/test_utility.cpp
+++ b/test/test_utility.cpp
@@ -31,7 +31,10 @@
 #include <gtest/gtest-message.h>              // for Message
 #include <gtest/gtest-test-part.h>            // for TestPartResult, SuiteA...
 #include <gtest/gtest_pred_impl.h>            // for Test, EXPECT_EQ, TestInfo
+#include <map>                                // for map, operator!=, _Rb_t...
+#include <stdexcept>                          // for out_of_range
 #include <string>                             // for allocator, string, ope...
+#include <utility>                            // for pair
 
 namespace caosdb::utility {
 using ::testing::ElementsAre;
@@ -73,9 +76,6 @@ TEST(test_utility, enum_names) {
   // Some non-working examples
   EXPECT_THROW_MESSAGE(getEnumValueFromName<caosdb::entity::Importance>("Invalid name"),
                        std::out_of_range, "Could not find enum value for string 'Invalid name'.");
-  enum e1 { a };
-  EXPECT_THROW_STARTS_WITH(getEnumNameFromValue<e1>(a), std::logic_error, "Enum type ");
-  EXPECT_THROW_STARTS_WITH(getEnumValueFromName<e1>("Hello!"), std::logic_error, "Enum type ");
 }
 
 } // namespace caosdb::utility
-- 
GitLab