From e2b49636521dfd04171c17e509b1bdba315b2a59 Mon Sep 17 00:00:00 2001
From: Sergey Yakubov <sergey.yakubov@desy.de>
Date: Fri, 26 Mar 2021 09:48:14 +0100
Subject: [PATCH] producer and consumer return version info

---
 .gitignore                                    |  2 +-
 CMakeModules/prepare_version.cmake            |  2 +-
 common/cpp/CMakeLists.txt                     |  3 ++
 .../asapo/common/{ => internal}/version.h.in  |  9 ++++-
 common/cpp/src/version/CMakeLists.txt         | 12 ++++++
 common/cpp/src/version/version.cpp            | 27 +++++++++++++
 consumer/api/cpp/CMakeLists.txt               |  2 +-
 .../api/cpp/include/asapo/asapo_consumer.h    |  1 -
 .../api/cpp/include/asapo/consumer/consumer.h |  8 ++++
 consumer/api/cpp/src/consumer_impl.cpp        | 38 +++++++++++++++++-
 consumer/api/cpp/src/consumer_impl.h          |  4 ++
 .../api/cpp/src/fabric_consumer_client.cpp    |  2 +-
 consumer/api/cpp/src/tcp_consumer_client.cpp  |  2 +-
 .../api/cpp/unittests/test_consumer_impl.cpp  | 27 +++++++++++++
 deploy/nomad_consul_docker/orchestr_config.py |  2 -
 .../asapo_discovery/protocols/protocols.go    |  8 ++--
 .../src/asapo_discovery/server/get_version.go | 38 +++++++++---------
 .../server/get_version_test.go                | 39 ++++++++++---------
 .../src/asapo_discovery/server/routes_test.go |  3 +-
 examples/consumer/getnext/getnext.cpp         |  1 -
 examples/pipeline/in_to_out/in_to_out.cpp     |  1 -
 .../dummy_data_producer.cpp                   |  1 -
 producer/api/cpp/CMakeLists.txt               |  2 +-
 .../api/cpp/include/asapo/asapo_producer.h    |  1 -
 .../api/cpp/include/asapo/producer/producer.h |  9 +++++
 producer/api/cpp/src/producer_impl.cpp        | 32 +++++++++++++--
 producer/api/cpp/src/producer_impl.h          |  7 ++++
 producer/api/cpp/src/producer_request.cpp     |  1 +
 .../cpp/src/receiver_discovery_service.cpp    |  2 +-
 .../api/cpp/unittests/test_producer_impl.cpp  | 31 +++++++++++++--
 .../src/main_eventmon.cpp                     |  2 +-
 receiver/src/main.cpp                         |  2 +-
 .../receiver_data_server_request_handler.cpp  |  2 +-
 .../request_handler_authorize.cpp             |  2 +-
 .../send_recv_streams/send_recv_streams.cpp   |  1 -
 .../beamtime_metadata/beamtime_metadata.cpp   |  1 -
 .../getlast_broker.cpp                        |  1 -
 tests/manual/producer_cpp/producer.cpp        |  2 +-
 38 files changed, 254 insertions(+), 76 deletions(-)
 rename common/cpp/include/asapo/common/{ => internal}/version.h.in (79%)
 create mode 100644 common/cpp/src/version/CMakeLists.txt
 create mode 100644 common/cpp/src/version/version.cpp

diff --git a/.gitignore b/.gitignore
index 5f8c3feae..d7c8d680d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -129,7 +129,7 @@ asapo_tools/pkg
 
 #version files
 
-common/cpp/include/asapo/common/version.h
+common/cpp/include/asapo/common/internal/version.h
 common/go/src/asapo_common/version/version_lib.go
 
 
diff --git a/CMakeModules/prepare_version.cmake b/CMakeModules/prepare_version.cmake
index 02bd80de2..7ef56e948 100644
--- a/CMakeModules/prepare_version.cmake
+++ b/CMakeModules/prepare_version.cmake
@@ -1,4 +1,4 @@
 string(TIMESTAMP TIMESTAMP "%H:%M:%S %d.%m.%Y UTC" UTC)
 
-configure_file(${PROJECT_SOURCE_DIR}/common/cpp/include/asapo/common/version.h.in ${PROJECT_SOURCE_DIR}/common/cpp/include/asapo/common/version.h @ONLY)
+configure_file(${PROJECT_SOURCE_DIR}/common/cpp/include/asapo/common/internal/version.h.in ${PROJECT_SOURCE_DIR}/common/cpp/include/asapo/common/internal/version.h @ONLY)
 configure_file(${PROJECT_SOURCE_DIR}/common/go/src/asapo_common/version/version_lib.go.in ${PROJECT_SOURCE_DIR}/common/go/src/asapo_common/version/version_lib.go @ONLY)
diff --git a/common/cpp/CMakeLists.txt b/common/cpp/CMakeLists.txt
index 4f29376de..8f90ff257 100644
--- a/common/cpp/CMakeLists.txt
+++ b/common/cpp/CMakeLists.txt
@@ -6,6 +6,9 @@ add_subdirectory(src/json_parser)
 
 add_subdirectory(src/data_structs)
 
+add_subdirectory(src/version)
+
+
 add_subdirectory(src/http_client)
 
 add_subdirectory(src/logger)
diff --git a/common/cpp/include/asapo/common/version.h.in b/common/cpp/include/asapo/common/internal/version.h.in
similarity index 79%
rename from common/cpp/include/asapo/common/version.h.in
rename to common/cpp/include/asapo/common/internal/version.h.in
index 5edb453a4..22f7de8e8 100644
--- a/common/cpp/include/asapo/common/version.h.in
+++ b/common/cpp/include/asapo/common/internal/version.h.in
@@ -4,10 +4,13 @@
 #include <iostream>
 #include "string.h"
 
-#include "data_structs.h"
+#include "asapo/common/data_structs.h"
+#include "asapo/common/error.h"
+#include "asapo/http_client/http_client.h"
 
 namespace asapo {
 
+
 const char kVersion[] = "@ASAPO_VERSION@@ASAPO_VERSION_COMMIT@";
 
 inline void ExitAfterPrintVersionIfNeeded(std::string prefix,int argc, char* argv[]) {
@@ -33,7 +36,9 @@ inline int VersionToNumber(const std::string& version) {
     return int(atof(version.c_str()+2)*1000);
 }
 
+Error ExtractVersionFromResponse(const std::string &response,
+                                 std::string* server_info,
+                                 bool* supported);
 }
 
-
 #endif //ASAPO_VERSION_H
diff --git a/common/cpp/src/version/CMakeLists.txt b/common/cpp/src/version/CMakeLists.txt
new file mode 100644
index 000000000..71abb5416
--- /dev/null
+++ b/common/cpp/src/version/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(TARGET_NAME version)
+set(SOURCE_FILES
+        version.cpp
+)
+
+################################
+# Library
+################################
+
+add_library(${TARGET_NAME} OBJECT ${SOURCE_FILES})
+target_include_directories(${TARGET_NAME} PUBLIC ${ASAPO_CXX_COMMON_INCLUDE_DIR}
+        ${CMAKE_SOURCE_DIR}/3d_party/rapidjson/include)
diff --git a/common/cpp/src/version/version.cpp b/common/cpp/src/version/version.cpp
new file mode 100644
index 000000000..1f9d2a392
--- /dev/null
+++ b/common/cpp/src/version/version.cpp
@@ -0,0 +1,27 @@
+#include "asapo/common/internal/version.h"
+#include "asapo/json_parser/json_parser.h"
+
+namespace asapo {
+
+Error ExtractVersionFromResponse(const std::string &response,
+                                 std::string* server_info,
+                                 bool* supported) {
+    JsonStringParser parser(response);
+    std::string server_version, current_client_protocol, client_supported;
+    Error err;
+    if ((err = parser.GetString("softwareVersion", &server_version))
+        || (err = parser.GetString("clientSupported", &client_supported))
+        || (err = parser.Embedded("clientProtocol").GetString("versionInfo", &current_client_protocol))) {
+        return err;
+    }
+    if (server_info) {
+        *server_info =
+            "Server version: " + server_version + ", protocol on server: " + current_client_protocol;
+    }
+    if (supported) {
+        *supported = client_supported == "yes";
+    }
+    return nullptr;
+}
+
+}
\ No newline at end of file
diff --git a/consumer/api/cpp/CMakeLists.txt b/consumer/api/cpp/CMakeLists.txt
index 24661f0da..47d30a20f 100644
--- a/consumer/api/cpp/CMakeLists.txt
+++ b/consumer/api/cpp/CMakeLists.txt
@@ -12,7 +12,7 @@ set(SOURCE_FILES
 # Library
 ################################
 add_library(${TARGET_NAME} STATIC ${SOURCE_FILES} $<TARGET_OBJECTS:system_io>
-            $<TARGET_OBJECTS:json_parser> $<TARGET_OBJECTS:data_structs> $<TARGET_OBJECTS:curl_http_client> )
+            $<TARGET_OBJECTS:json_parser> $<TARGET_OBJECTS:data_structs> $<TARGET_OBJECTS:version>  $<TARGET_OBJECTS:curl_http_client> )
 
 target_include_directories(${TARGET_NAME} PUBLIC include ${ASAPO_CXX_COMMON_INCLUDE_DIR}  ${LIBFABRIC_INCLUDE_DIR} ${CURL_INCLUDE_DIRS})
 
diff --git a/consumer/api/cpp/include/asapo/asapo_consumer.h b/consumer/api/cpp/include/asapo/asapo_consumer.h
index e9dee4e9b..176d1d562 100644
--- a/consumer/api/cpp/include/asapo/asapo_consumer.h
+++ b/consumer/api/cpp/include/asapo/asapo_consumer.h
@@ -3,7 +3,6 @@
 
 #include "asapo/consumer/consumer.h"
 #include "asapo/consumer/consumer_error.h"
-#include "asapo/common/version.h"
 #include <ostream>
 
 #endif //ASAPO_ASAPO_CONSUMER_H
diff --git a/consumer/api/cpp/include/asapo/consumer/consumer.h b/consumer/api/cpp/include/asapo/consumer/consumer.h
index 2cd1d71b6..dba769567 100644
--- a/consumer/api/cpp/include/asapo/consumer/consumer.h
+++ b/consumer/api/cpp/include/asapo/consumer/consumer.h
@@ -27,6 +27,14 @@ class Consumer {
       \return nullptr of command was successful, otherwise error.
     */
     virtual Error ResetLastReadMarker(std::string group_id, std::string stream) = 0;
+  //! Return version
+  /*!
+    \param client_info - for client version
+    \param server_info - for server
+    \param supported - set to true if client is supported by server
+    \return nullptr of command was successful, otherwise error.
+  */
+    virtual Error GetVersionInfo(std::string* client_info,std::string* server_info, bool* supported) = 0;
 
     virtual Error SetLastReadMarker(std::string group_id, uint64_t value, std::string stream) = 0;
 
diff --git a/consumer/api/cpp/src/consumer_impl.cpp b/consumer/api/cpp/src/consumer_impl.cpp
index 5df785afa..6ac36b34e 100644
--- a/consumer/api/cpp/src/consumer_impl.cpp
+++ b/consumer/api/cpp/src/consumer_impl.cpp
@@ -12,7 +12,7 @@
 #include "fabric_consumer_client.h"
 #include "rds_response_error.h"
 
-#include "asapo/common/version.h"
+#include "asapo/common/internal/version.h"
 
 using std::chrono::system_clock;
 
@@ -734,7 +734,7 @@ Error ConsumerImpl::UpdateFolderTokenIfNeeded(bool ignore_existing) {
 RequestInfo ConsumerImpl::CreateFolderTokenRequest() const {
     RequestInfo ri;
     ri.host = endpoint_;
-    ri.api = "/asapo-authorizer/"+kConsumerProtocol.GetAuthorizerVersion()+"/folder";
+    ri.api = "/asapo-authorizer/" + kConsumerProtocol.GetAuthorizerVersion() + "/folder";
     ri.post = true;
     ri.body =
         "{\"Folder\":\"" + source_path_ + "\",\"BeamtimeId\":\"" + source_credentials_.beamtime_id + "\",\"Token\":\""
@@ -909,4 +909,38 @@ RequestInfo ConsumerImpl::GetSizeRequestForSingleMessagesStream(std::string &str
     return ri;
 }
 
+RequestInfo ConsumerImpl::GetVersionRequest() const {
+    RequestInfo ri;
+    ri.host = endpoint_;
+    ri.api = "/asapo-discovery/" + kConsumerProtocol.GetDiscoveryVersion() + "/version";
+    ri.extra_params = "&client=consumer&protocol=" + kConsumerProtocol.GetVersion();
+    return ri;
+}
+
+Error ConsumerImpl::GetServerVersionInfo(std::string* server_info, bool* supported) {
+    auto ri = GetVersionRequest();
+    RequestOutput output;
+    auto err = ProcessRequest(&output, ri, nullptr);
+    if (err) {
+        return err;
+    }
+    return ExtractVersionFromResponse(output.string_output,server_info,supported);
 }
+
+Error ConsumerImpl::GetVersionInfo(std::string* client_info, std::string* server_info, bool* supported) {
+    if (client_info == nullptr && server_info == nullptr && supported == nullptr) {
+        return ConsumerErrorTemplates::kWrongInput.Generate("missing parameters");
+    }
+    if (client_info != nullptr) {
+        *client_info =
+            "software version: " + std::string(kVersion) + ", consumer protocol: " + kConsumerProtocol.GetVersion();
+    }
+
+    if (server_info != nullptr || supported != nullptr) {
+        return GetServerVersionInfo(server_info,supported);
+    }
+
+    return nullptr;
+}
+
+}
\ No newline at end of file
diff --git a/consumer/api/cpp/src/consumer_impl.h b/consumer/api/cpp/src/consumer_impl.h
index 94217754e..ef0ee3ac8 100644
--- a/consumer/api/cpp/src/consumer_impl.h
+++ b/consumer/api/cpp/src/consumer_impl.h
@@ -84,6 +84,7 @@ class ConsumerImpl final : public asapo::Consumer {
 
     Error GetById(uint64_t id, MessageMeta* info, MessageData* data, std::string stream) override;
 
+    Error GetVersionInfo(std::string* client_info,std::string* server_info, bool* supported) override;
 
     void SetTimeout(uint64_t timeout_ms) override;
     void ForceNoRdma() override;
@@ -146,6 +147,7 @@ class ConsumerImpl final : public asapo::Consumer {
 
     uint64_t GetCurrentCount(std::string stream, const RequestInfo& ri, Error* err);
     RequestInfo GetStreamListRequest(const std::string &from, const StreamFilter &filter) const;
+    Error GetServerVersionInfo(std::string* server_info, bool* supported) ;
 
     std::string endpoint_;
     std::string current_broker_uri_;
@@ -169,6 +171,8 @@ class ConsumerImpl final : public asapo::Consumer {
   RequestInfo GetSizeRequestForDatasetStream(std::string &stream, bool include_incomplete) const;
   uint64_t ParseGetCurrentCountResponce(Error* err, const std::string &responce) const;
   RequestInfo GetDiscoveryRequest(const std::string &service_name) const;
+  RequestInfo GetVersionRequest() const;
+
 };
 
 }
diff --git a/consumer/api/cpp/src/fabric_consumer_client.cpp b/consumer/api/cpp/src/fabric_consumer_client.cpp
index ed3524a99..774513e1f 100644
--- a/consumer/api/cpp/src/fabric_consumer_client.cpp
+++ b/consumer/api/cpp/src/fabric_consumer_client.cpp
@@ -3,7 +3,7 @@
 #include <iostream>
 #include "fabric_consumer_client.h"
 #include "rds_response_error.h"
-#include "asapo/common/version.h"
+#include "asapo/common/internal/version.h"
 
 using namespace asapo;
 
diff --git a/consumer/api/cpp/src/tcp_consumer_client.cpp b/consumer/api/cpp/src/tcp_consumer_client.cpp
index 5846a4a94..d58f05d6b 100644
--- a/consumer/api/cpp/src/tcp_consumer_client.cpp
+++ b/consumer/api/cpp/src/tcp_consumer_client.cpp
@@ -2,7 +2,7 @@
 #include "asapo/io/io_factory.h"
 #include "asapo/common/networking.h"
 #include "rds_response_error.h"
-#include "asapo/common/version.h"
+#include "asapo/common/internal/version.h"
 
 namespace asapo {
 
diff --git a/consumer/api/cpp/unittests/test_consumer_impl.cpp b/consumer/api/cpp/unittests/test_consumer_impl.cpp
index c2048a67d..6ad5448e5 100644
--- a/consumer/api/cpp/unittests/test_consumer_impl.cpp
+++ b/consumer/api/cpp/unittests/test_consumer_impl.cpp
@@ -14,6 +14,7 @@
 #include "asapo/http_client/http_error.h"
 #include "mocking.h"
 #include "../src/tcp_consumer_client.h"
+#include "asapo/common/internal/version.h"
 
 using asapo::ConsumerFactory;
 using asapo::Consumer;
@@ -1342,4 +1343,30 @@ TEST_F(ConsumerImplTests, GetCurrentDataSetCounteUsesCorrectUri) {
     ASSERT_THAT(size, Eq(10));
 }
 
+
+TEST_F(ConsumerImplTests, GetVersionInfoClientOnly) {
+    std::string client_info;
+    auto err = consumer->GetVersionInfo(&client_info,nullptr,nullptr);
+    ASSERT_THAT(err, Eq(nullptr));
+    ASSERT_THAT(client_info, HasSubstr(std::string(asapo::kVersion)));
+    ASSERT_THAT(client_info, HasSubstr(asapo::kConsumerProtocol.GetVersion()));
+}
+
+TEST_F(ConsumerImplTests, GetVersionInfoWithServer) {
+
+    std::string result = R"({"softwareVersion":"20.03.1, build 7a9294ad","clientSupported":"no", "clientProtocol":{"versionInfo":"v0.2"}})";
+
+    EXPECT_CALL(mock_http_client, Get_t(HasSubstr(expected_server_uri + "/asapo-discovery/v0.1/version?token=token&client=consumer&protocol=v0.1"), _,_)).WillOnce(DoAll(
+        SetArgPointee<1>(HttpCode::OK),
+        SetArgPointee<2>(nullptr),
+        Return(result)));
+
+    std::string client_info,server_info;
+    auto err = consumer->GetVersionInfo(&client_info,&server_info,nullptr);
+    ASSERT_THAT(err, Eq(nullptr));
+    ASSERT_THAT(server_info, HasSubstr("20.03.1"));
+    ASSERT_THAT(server_info, HasSubstr("v0.2"));
+}
+
+
 }
diff --git a/deploy/nomad_consul_docker/orchestr_config.py b/deploy/nomad_consul_docker/orchestr_config.py
index 5e91651d3..52f5da0e6 100644
--- a/deploy/nomad_consul_docker/orchestr_config.py
+++ b/deploy/nomad_consul_docker/orchestr_config.py
@@ -59,8 +59,6 @@ def process_file(file_in,file_out):
     filein = open(file_in)
     src = Template(filein.read())
     d = set_parameters()
-    print d
-
     with open(file_out, "w") as out:
         out.write(src.substitute(d))
 
diff --git a/discovery/src/asapo_discovery/protocols/protocols.go b/discovery/src/asapo_discovery/protocols/protocols.go
index 0d52d1737..ada29e7f0 100644
--- a/discovery/src/asapo_discovery/protocols/protocols.go
+++ b/discovery/src/asapo_discovery/protocols/protocols.go
@@ -20,8 +20,8 @@ type Protocol struct {
 }
 
 type ProtocolInfo  struct {
-	Info string
-	MicroserviceAPis map[string]string
+	VersionInfo      string            `json:"versionInfo"`
+	MicroservicesApi map[string]string `json:"microservicesApi"`
 }
 
 func (p *Protocol) IsValid() (hint string, ok bool) {
@@ -76,8 +76,8 @@ func GetSupportedProtocolsArray(client string) ([]ProtocolInfo, error) {
 	res:=make([]ProtocolInfo,0)
 	for _,protocol := range protocols {
 		var info ProtocolInfo
-		info.Info = protocol.GetString()
-		info.MicroserviceAPis = protocol.MicroserviceAPis
+		info.VersionInfo = protocol.GetString()
+		info.MicroservicesApi = protocol.MicroserviceAPis
 		res = append(res, info)
 	}
 	return res,nil
diff --git a/discovery/src/asapo_discovery/server/get_version.go b/discovery/src/asapo_discovery/server/get_version.go
index 2c9d937ae..3e2abebd5 100644
--- a/discovery/src/asapo_discovery/server/get_version.go
+++ b/discovery/src/asapo_discovery/server/get_version.go
@@ -11,12 +11,10 @@ import (
 )
 
 type versionInfo struct {
-	CoreServices               string
-	ClientConsumerProtocol     protocols.ProtocolInfo
-	ClientProducerProtocol     protocols.ProtocolInfo
-	ClientSupported            string
-	SupportedProducerProtocols []protocols.ProtocolInfo
-	SupportedConsumerProtocols []protocols.ProtocolInfo
+	SoftwareVersion            string `json:"softwareVersion"`
+	ClientProtocol     protocols.ProtocolInfo `json:"clientProtocol"`
+	ClientSupported            string `json:"clientSupported"`
+	SupportedProtocols []protocols.ProtocolInfo `json:"supportedProtocols"`
 }
 
 func extractProtocol(r *http.Request) (string, error) {
@@ -55,23 +53,25 @@ func checkDiscoveryApiVersion(w http.ResponseWriter, r *http.Request) bool {
 }
 
 func getVersionInfo(client string, ver string) (versionInfo, error) {
-	info, err := getCoreInfo()
+	info, err := getCoreInfo(client)
 	if err != nil {
 		return versionInfo{}, err
 	}
+	if ver=="" {
+		return info, nil
+	}
 	updateClientInfo(client, ver, &info)
 	return info, nil
 }
 
-func getCoreInfo() (versionInfo, error) {
+func getCoreInfo(client string) (versionInfo, error) {
 	var info versionInfo
-	info.CoreServices = version.GetVersion()
-	var err error
-	info.SupportedConsumerProtocols, err = protocols.GetSupportedProtocolsArray("consumer")
-	if err != nil {
-		return versionInfo{}, err
+	info.SoftwareVersion = version.GetVersion()
+	if client=="" {
+		return info, nil
 	}
-	info.SupportedProducerProtocols, err = protocols.GetSupportedProtocolsArray("producer")
+	var err error
+	info.SupportedProtocols, err = protocols.GetSupportedProtocolsArray(client)
 	if err != nil {
 		return versionInfo{}, err
 	}
@@ -85,10 +85,10 @@ func updateClientInfo(client string, ver string, info *versionInfo) {
 	pInfo,valid := getProtocolInfo(client, ver, info)
 	setSupported(valid, info)
 	if client == "consumer" {
-		info.ClientConsumerProtocol = pInfo
+		info.ClientProtocol = pInfo
 	} else
 	if client == "producer" {
-		info.ClientProducerProtocol = pInfo
+		info.ClientProtocol = pInfo
 	}
 }
 
@@ -103,13 +103,13 @@ func setSupported(valid bool, info *versionInfo) {
 func getProtocolInfo(client string, ver string, info *versionInfo) (pInfo protocols.ProtocolInfo, valid bool) {
 	protocol, err := protocols.FindProtocol(client, ver)
 	if err != nil {
-		pInfo.Info = ver + " (" + err.Error() + ")"
+		pInfo.VersionInfo = ver + " (" + err.Error() + ")"
 		valid = false
 	} else {
 		var hint string
 		hint, valid = protocol.IsValid()
-		pInfo.Info = ver + " (" + hint + ")"
-		pInfo.MicroserviceAPis = protocol.MicroserviceAPis
+		pInfo.VersionInfo = ver + " (" + hint + ")"
+		pInfo.MicroservicesApi = protocol.MicroserviceAPis
 	}
 	return
 }
diff --git a/discovery/src/asapo_discovery/server/get_version_test.go b/discovery/src/asapo_discovery/server/get_version_test.go
index cf4b0db1c..5a1e99c8a 100644
--- a/discovery/src/asapo_discovery/server/get_version_test.go
+++ b/discovery/src/asapo_discovery/server/get_version_test.go
@@ -19,29 +19,31 @@ var versionTests = []struct {
 	message string
 }{
 	{"", versionInfo{
-		CoreServices:               coreVer,
-		ClientConsumerProtocol:     protocols.ProtocolInfo{},
-		ClientProducerProtocol:     protocols.ProtocolInfo{},
-		ClientSupported:            "",
+		SoftwareVersion:        coreVer,
+		ClientProtocol: protocols.ProtocolInfo{},
+		ClientSupported:        "",
 	}, http.StatusOK, "no client"},
+	{"?client=consumer", versionInfo{
+		SoftwareVersion: coreVer,
+		ClientProtocol:     protocols.ProtocolInfo{"", nil},
+		ClientSupported:            "no",
+	}, http.StatusOK, "consumer client, no protocol"},
+
 	{"?client=consumer&protocol=v0.1", versionInfo{
-		CoreServices:               coreVer,
-		ClientConsumerProtocol:     protocols.ProtocolInfo{"v0.1 (current)",
+		SoftwareVersion: coreVer,
+		ClientProtocol:     protocols.ProtocolInfo{"v0.1 (current)",
 			map[string]string{"Authorizer":"v0.1", "Broker":"v0.1", "Data cache service":"v0.1", "Discovery":"v0.1", "File Transfer":"v0.1"}},
-		ClientProducerProtocol:     protocols.ProtocolInfo{},
 		ClientSupported:            "yes",
 	}, http.StatusOK, "consumer client"},
 	{"?client=producer&protocol=v0.1", versionInfo{
-		CoreServices:               coreVer,
-		ClientProducerProtocol:     protocols.ProtocolInfo{"v0.1 (current)",map[string]string{"Discovery":"v0.1", "Receiver":"v0.1"}},
-		ClientConsumerProtocol:     protocols.ProtocolInfo{},
-		ClientSupported:            "yes",
+		SoftwareVersion:        coreVer,
+		ClientProtocol: protocols.ProtocolInfo{"v0.1 (current)",map[string]string{"Discovery":"v0.1", "Receiver":"v0.1"}},
+		ClientSupported:        "yes",
 	}, http.StatusOK, "producer client"},
 	{"?client=producer&protocol=v0.2", versionInfo{
-		CoreServices:               coreVer,
-		ClientProducerProtocol:     protocols.ProtocolInfo{"v0.2 (unknown protocol)",nil},
-		ClientConsumerProtocol:     protocols.ProtocolInfo{},
-		ClientSupported:            "no",
+		SoftwareVersion:        coreVer,
+		ClientProtocol: protocols.ProtocolInfo{"v0.2 (unknown protocol)",nil},
+		ClientSupported:        "no",
 	}, http.StatusOK, "producer client unknown"},
 }
 
@@ -53,9 +55,10 @@ func TestVersionTests(t *testing.T) {
 			var info versionInfo
 			json.Unmarshal(w.Body.Bytes(), &info)
 			fmt.Println(w.Body.String())
-			assert.Equal(t, test.result.ClientConsumerProtocol,info.ClientConsumerProtocol, test.message)
-			assert.Equal(t, true,len(info.SupportedProducerProtocols)>0, test.message)
-			assert.Equal(t, true,len(info.SupportedConsumerProtocols)>0, test.message)
+			assert.Equal(t, test.result.ClientProtocol,info.ClientProtocol, test.message)
+			if test.message!="no client" {
+				assert.Equal(t, true,len(info.SupportedProtocols)>0, test.message)
+			}
 		}
 	}
 }
diff --git a/discovery/src/asapo_discovery/server/routes_test.go b/discovery/src/asapo_discovery/server/routes_test.go
index 34a020ca5..e72d0c55a 100644
--- a/discovery/src/asapo_discovery/server/routes_test.go
+++ b/discovery/src/asapo_discovery/server/routes_test.go
@@ -145,7 +145,6 @@ func (suite *GetServicesTestSuite) TestGetVersions() {
 	suite.Equal(http.StatusOK, w.Code, "code ok")
 	// we dont really check what it returns, just that route is ok
 	suite.Contains(w.Body.String(), version.GetVersion(), "core version")
-	suite.Contains(w.Body.String(), "SupportedConsumerProtocols", "consumer protocols")
-	suite.Contains(w.Body.String(), "SupportedProducerProtocols", "producers protocols")
+	suite.Contains(w.Body.String(), "supportedProtocols", "protocols")
 	assertExpectations(suite.T())
 }
diff --git a/examples/consumer/getnext/getnext.cpp b/examples/consumer/getnext/getnext.cpp
index ae48a1a29..d30e87e57 100644
--- a/examples/consumer/getnext/getnext.cpp
+++ b/examples/consumer/getnext/getnext.cpp
@@ -265,7 +265,6 @@ void TryGetStream(Args* args) {
 }
 
 int main(int argc, char* argv[]) {
-    asapo::ExitAfterPrintVersionIfNeeded("GetNext consumer Example", argc, argv);
     Args params;
     params.datasets = false;
     if (argc != 8 && argc != 9) {
diff --git a/examples/pipeline/in_to_out/in_to_out.cpp b/examples/pipeline/in_to_out/in_to_out.cpp
index 801d9f32a..e96985a02 100644
--- a/examples/pipeline/in_to_out/in_to_out.cpp
+++ b/examples/pipeline/in_to_out/in_to_out.cpp
@@ -206,7 +206,6 @@ std::unique_ptr<asapo::Producer> CreateProducer(const Args &args) {
 }
 
 int main(int argc, char* argv[]) {
-    asapo::ExitAfterPrintVersionIfNeeded("GetNext consumer Example", argc, argv);
     Args args;
     if (argc != 11) {
         std::cout << "Usage: " + std::string{argv[0]}
diff --git a/examples/producer/dummy-data-producer/dummy_data_producer.cpp b/examples/producer/dummy-data-producer/dummy_data_producer.cpp
index d983f01e2..560cd20ba 100644
--- a/examples/producer/dummy-data-producer/dummy_data_producer.cpp
+++ b/examples/producer/dummy-data-producer/dummy_data_producer.cpp
@@ -69,7 +69,6 @@ void TryGetDataSourceAndToken(Args* args) {
 
 
 void ProcessCommandArguments(int argc, char* argv[], Args* args) {
-    asapo::ExitAfterPrintVersionIfNeeded("Dummy Data Producer", argc, argv);
     if (argc != 8 && argc != 9) {
         std::cout <<
                   "Usage: " << argv[0] <<
diff --git a/producer/api/cpp/CMakeLists.txt b/producer/api/cpp/CMakeLists.txt
index 896c05265..24b572235 100644
--- a/producer/api/cpp/CMakeLists.txt
+++ b/producer/api/cpp/CMakeLists.txt
@@ -14,7 +14,7 @@ set(SOURCE_FILES
 # Library
 ################################
 add_library(${TARGET_NAME} STATIC ${SOURCE_FILES} $<TARGET_OBJECTS:system_io> $<TARGET_OBJECTS:logger> $<TARGET_OBJECTS:json_parser>
-        $<TARGET_OBJECTS:curl_http_client> $<TARGET_OBJECTS:request_pool> $<TARGET_OBJECTS:data_structs>)
+        $<TARGET_OBJECTS:curl_http_client> $<TARGET_OBJECTS:request_pool> $<TARGET_OBJECTS:data_structs> $<TARGET_OBJECTS:version>)
 target_include_directories(${TARGET_NAME} PUBLIC include ${ASAPO_CXX_COMMON_INCLUDE_DIR})
 target_link_libraries(${TARGET_NAME} ${CURL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
 
diff --git a/producer/api/cpp/include/asapo/asapo_producer.h b/producer/api/cpp/include/asapo/asapo_producer.h
index 1e5ea176c..152abe230 100644
--- a/producer/api/cpp/include/asapo/asapo_producer.h
+++ b/producer/api/cpp/include/asapo/asapo_producer.h
@@ -2,7 +2,6 @@
 #define ASAPO_ASAPO_PRODUCER_H
 
 #include "asapo/common/io_error.h"
-#include "asapo/common/version.h"
 
 #include "asapo/producer/producer.h"
 #include "asapo/producer/producer_error.h"
diff --git a/producer/api/cpp/include/asapo/producer/producer.h b/producer/api/cpp/include/asapo/producer/producer.h
index c8de7d637..a2fde18eb 100644
--- a/producer/api/cpp/include/asapo/producer/producer.h
+++ b/producer/api/cpp/include/asapo/producer/producer.h
@@ -23,6 +23,15 @@ class Producer {
 
     virtual ~Producer() = default;
 
+  //! Return version
+  /*!
+    \param client_info - for client version
+    \param server_info - for server
+    \param supported - set to true if client is supported by server
+    \return nullptr of command was successful, otherwise error.
+  */
+  virtual Error GetVersionInfo(std::string* client_info,std::string* server_info, bool* supported) const = 0;
+
     //! Get stream information from receiver
     /*!
       \param stream - stream to send messages to
diff --git a/producer/api/cpp/src/producer_impl.cpp b/producer/api/cpp/src/producer_impl.cpp
index cba069bb7..6779fbacb 100644
--- a/producer/api/cpp/src/producer_impl.cpp
+++ b/producer/api/cpp/src/producer_impl.cpp
@@ -1,16 +1,16 @@
 #include <iostream>
-#include <iostream>
 #include <cstring>
 #include <future>
 
 #include "producer_impl.h"
 #include "producer_logger.h"
-#include "asapo/io/io_factory.h"
 #include "asapo/producer/producer_error.h"
 #include "producer_request_handler_factory.h"
 #include "producer_request.h"
 #include "asapo/common/data_structs.h"
 #include "asapo/request/request_pool_error.h"
+#include "asapo/http_client/http_client.h"
+#include "asapo/common/internal/version.h"
 
 namespace  asapo {
 
@@ -18,7 +18,7 @@ const size_t ProducerImpl::kDiscoveryServiceUpdateFrequencyMs = 10000; // 10s
 
 ProducerImpl::ProducerImpl(std::string endpoint, uint8_t n_processing_threads, uint64_t timeout_ms,
                            asapo::RequestHandlerType type):
-    log__{GetDefaultProducerLogger()}, timeout_ms_{timeout_ms} {
+    log__{GetDefaultProducerLogger()},httpclient__{DefaultHttpClient()}, timeout_ms_{timeout_ms},endpoint_{endpoint} {
     switch (type) {
     case RequestHandlerType::kTcp:
         discovery_service_.reset(new ReceiverDiscoveryService{endpoint, ProducerImpl::kDiscoveryServiceUpdateFrequencyMs});
@@ -392,5 +392,31 @@ void ProducerImpl::SetRequestsQueueLimits(uint64_t size, uint64_t volume) {
     request_pool__->SetLimits(RequestPoolLimits{size,volume});
 }
 
+Error ProducerImpl::GetVersionInfo(std::string* client_info, std::string* server_info, bool* supported) const {
+    if (client_info == nullptr && server_info == nullptr && supported == nullptr) {
+        return ProducerErrorTemplates::kWrongInput.Generate("missing parameters");
+    }
+    if (client_info != nullptr) {
+        *client_info =
+            "software version: " + std::string(kVersion) + ", consumer protocol: " + kProducerProtocol.GetVersion();
+    }
+
+    if (server_info != nullptr || supported != nullptr) {
+        return GetServerVersionInfo(server_info, supported);
+    }
+    return nullptr;
+}
+
+Error ProducerImpl::GetServerVersionInfo(std::string* server_info,
+                                         bool* supported) const {
+    auto endpoint = endpoint_ +"/asapo-discovery/"+kProducerProtocol.GetDiscoveryVersion()+"/version?client=producer&protocol="+kProducerProtocol.GetVersion();
+    HttpCode  code;
+    Error err;
+    auto response = httpclient__->Get(endpoint, &code, &err);
+    if (err) {
+        return err;
+    }
+    return ExtractVersionFromResponse(response,server_info,supported);
+}
 
 }
\ No newline at end of file
diff --git a/producer/api/cpp/src/producer_impl.h b/producer/api/cpp/src/producer_impl.h
index a60b59c7d..53fda3df0 100644
--- a/producer/api/cpp/src/producer_impl.h
+++ b/producer/api/cpp/src/producer_impl.h
@@ -30,6 +30,9 @@ class ProducerImpl : public Producer {
   ProducerImpl(const ProducerImpl &) = delete;
   ProducerImpl &operator=(const ProducerImpl &) = delete;
 
+
+  Error GetVersionInfo(std::string* client_info,std::string* server_info, bool* supported) const override;
+
   StreamInfo GetStreamInfo(std::string stream, uint64_t timeout_ms, Error* err) const override;
   StreamInfo GetLastStream(uint64_t timeout_ms, Error* err) const override;
 
@@ -56,6 +59,7 @@ class ProducerImpl : public Producer {
                                   RequestCallback callback) override;
 
   AbstractLogger* log__;
+  std::unique_ptr<HttpClient> httpclient__;
   std::unique_ptr<RequestPool> request_pool__;
 
   Error SetCredentials(SourceCredentials source_cred) override;
@@ -74,6 +78,9 @@ class ProducerImpl : public Producer {
                                                uint64_t ingest_mode);
   std::string source_cred_string_;
   uint64_t timeout_ms_;
+  std::string endpoint_;
+  Error GetServerVersionInfo(std::string* server_info,
+                             bool* supported) const;
 };
 
 struct StreamInfoResult {
diff --git a/producer/api/cpp/src/producer_request.cpp b/producer/api/cpp/src/producer_request.cpp
index 32dfaa87e..7d41d0d44 100644
--- a/producer/api/cpp/src/producer_request.cpp
+++ b/producer/api/cpp/src/producer_request.cpp
@@ -1,5 +1,6 @@
 #include <asapo/asapo_producer.h>
 #include "producer_request.h"
+#include "asapo/common/internal/version.h"
 
 namespace asapo {
 
diff --git a/producer/api/cpp/src/receiver_discovery_service.cpp b/producer/api/cpp/src/receiver_discovery_service.cpp
index 9a92bf974..b1130b7ec 100644
--- a/producer/api/cpp/src/receiver_discovery_service.cpp
+++ b/producer/api/cpp/src/receiver_discovery_service.cpp
@@ -6,7 +6,7 @@
 
 #include "producer_logger.h"
 #include "asapo/json_parser/json_parser.h"
-#include "asapo/common/version.h"
+#include "asapo/common/internal/version.h"
 
 namespace  asapo {
 
diff --git a/producer/api/cpp/unittests/test_producer_impl.cpp b/producer/api/cpp/unittests/test_producer_impl.cpp
index bdea491ac..5219352f1 100644
--- a/producer/api/cpp/unittests/test_producer_impl.cpp
+++ b/producer/api/cpp/unittests/test_producer_impl.cpp
@@ -1,5 +1,3 @@
-#pragma clang diagnostic push
-#pragma ide diagnostic ignored "InfiniteRecursion"
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
 
@@ -11,6 +9,7 @@
 
 #include "../src/request_handler_tcp.h"
 #include "asapo/request/request_pool_error.h"
+#include "asapo/unittests/MockHttpClient.h"
 
 #include "mocking.h"
 
@@ -26,10 +25,13 @@ using ::testing::Ne;
 using ::testing::Mock;
 using ::testing::InSequence;
 using ::testing::HasSubstr;
+using testing::SetArgPointee;
 
 
 using asapo::RequestPool;
 using asapo::ProducerRequest;
+using asapo::MockHttpClient;
+
 
 MATCHER_P10(M_CheckSendRequest, op_code, source_credentials, metadata, file_id, file_size, message, stream,
             ingest_mode,
@@ -56,6 +58,8 @@ TEST(ProducerImpl, Constructor) {
     asapo::ProducerImpl producer{"", 4, 3600000, asapo::RequestHandlerType::kTcp};
     ASSERT_THAT(dynamic_cast<asapo::AbstractLogger*>(producer.log__), Ne(nullptr));
     ASSERT_THAT(dynamic_cast<asapo::RequestPool*>(producer.request_pool__.get()), Ne(nullptr));
+    ASSERT_THAT(dynamic_cast<const asapo::HttpClient*>(producer.httpclient__.get()), Ne(nullptr));
+
 }
 
 class ProducerImplTests : public testing::Test {
@@ -64,7 +68,8 @@ class ProducerImplTests : public testing::Test {
   asapo::ProducerRequestHandlerFactory factory{&service};
   testing::NiceMock<asapo::MockLogger> mock_logger;
   testing::NiceMock<MockRequestPull> mock_pull{&factory, &mock_logger};
-  asapo::ProducerImpl producer{"", 1, 3600000, asapo::RequestHandlerType::kTcp};
+  std::string expected_server_uri = "test:8400";
+  asapo::ProducerImpl producer{expected_server_uri, 1, 3600000, asapo::RequestHandlerType::kTcp};
   uint64_t expected_size = 100;
   uint64_t expected_id = 10;
   uint64_t expected_dataset_id = 100;
@@ -88,9 +93,15 @@ class ProducerImplTests : public testing::Test {
   std::string expected_fullpath = "filename";
   bool expected_managed_memory = true;
   bool expected_unmanaged_memory = false;
+
+  MockHttpClient* mock_http_client;
+
   void SetUp() override {
       producer.log__ = &mock_logger;
       producer.request_pool__ = std::unique_ptr<RequestPool>{&mock_pull};
+      mock_http_client = new MockHttpClient;
+      producer.httpclient__.reset(mock_http_client);
+
   }
   void TearDown() override {
       producer.request_pool__.release();
@@ -510,8 +521,20 @@ TEST_F(ProducerImplTests, ReturnDataIfCanotAddToQueue) {
 
 }
 
+TEST_F(ProducerImplTests, GetVersionInfoWithServer) {
+
+    std::string result = R"({"softwareVersion":"20.03.1, build 7a9294ad","clientSupported":"no", "clientProtocol":{"versionInfo":"v0.2"}})";
 
+    EXPECT_CALL(*mock_http_client, Get_t(HasSubstr(expected_server_uri + "/asapo-discovery/v0.1/version?client=producer&protocol=v0.1"), _,_)).WillOnce(DoAll(
+        SetArgPointee<1>(asapo::HttpCode::OK),
+        SetArgPointee<2>(nullptr),
+        Return(result)));
 
+    std::string client_info,server_info;
+    auto err = producer.GetVersionInfo(&client_info,&server_info,nullptr);
+    ASSERT_THAT(err, Eq(nullptr));
+    ASSERT_THAT(server_info, HasSubstr("20.03.1"));
+    ASSERT_THAT(server_info, HasSubstr("v0.2"));
 }
 
-#pragma clang diagnostic pop
\ No newline at end of file
+}
diff --git a/producer/event_monitor_producer/src/main_eventmon.cpp b/producer/event_monitor_producer/src/main_eventmon.cpp
index d30e479a8..72446fa52 100644
--- a/producer/event_monitor_producer/src/main_eventmon.cpp
+++ b/producer/event_monitor_producer/src/main_eventmon.cpp
@@ -15,7 +15,7 @@
 #include "asapo/preprocessor/definitions.h"
 
 #include "asapo/io/io_factory.h"
-#include "asapo/common/version.h"
+#include "asapo/common/internal/version.h"
 
 using asapo::Producer;
 using asapo::EventMonConfigFactory;
diff --git a/receiver/src/main.cpp b/receiver/src/main.cpp
index 2f119b200..e00c94fbd 100644
--- a/receiver/src/main.cpp
+++ b/receiver/src/main.cpp
@@ -6,7 +6,7 @@
 #include "receiver_config.h"
 
 #include "receiver_data_server/receiver_data_server_logger.h"
-#include "asapo/common/version.h"
+#include "asapo/common/internal/version.h"
 
 #include "receiver_data_server/receiver_data_server.h"
 #include "receiver_data_server/net_server/rds_tcp_server.h"
diff --git a/receiver/src/receiver_data_server/request_handler/receiver_data_server_request_handler.cpp b/receiver/src/receiver_data_server/request_handler/receiver_data_server_request_handler.cpp
index f4e5af4b6..0f8f387fa 100644
--- a/receiver/src/receiver_data_server/request_handler/receiver_data_server_request_handler.cpp
+++ b/receiver/src/receiver_data_server/request_handler/receiver_data_server_request_handler.cpp
@@ -1,7 +1,7 @@
 #include "receiver_data_server_request_handler.h"
 
 #include "../receiver_data_server_error.h"
-#include "asapo/common/version.h"
+#include "asapo/common/internal/version.h"
 namespace asapo {
 
 ReceiverDataServerRequestHandler::ReceiverDataServerRequestHandler(RdsNetServer* server,
diff --git a/receiver/src/request_handler/request_handler_authorize.cpp b/receiver/src/request_handler/request_handler_authorize.cpp
index c0294754c..314b351a4 100644
--- a/receiver/src/request_handler/request_handler_authorize.cpp
+++ b/receiver/src/request_handler/request_handler_authorize.cpp
@@ -4,7 +4,7 @@
 #include "../request.h"
 
 #include "asapo/json_parser/json_parser.h"
-#include "asapo/common/version.h"
+#include "asapo/common/internal/version.h"
 
 using std::chrono::system_clock;
 
diff --git a/tests/automatic/full_chain/send_recv_streams/send_recv_streams.cpp b/tests/automatic/full_chain/send_recv_streams/send_recv_streams.cpp
index 9e1820083..7858801dc 100644
--- a/tests/automatic/full_chain/send_recv_streams/send_recv_streams.cpp
+++ b/tests/automatic/full_chain/send_recv_streams/send_recv_streams.cpp
@@ -73,7 +73,6 @@ ProducerPtr CreateProducer(const Args& args) {
 }
 
 int main(int argc, char* argv[]) {
-    asapo::ExitAfterPrintVersionIfNeeded("GetNext consumer Example", argc, argv);
     Args args;
     if (argc != 4) {
         std::cout << "Usage: " + std::string{argv[0]}
diff --git a/tests/automatic/producer/beamtime_metadata/beamtime_metadata.cpp b/tests/automatic/producer/beamtime_metadata/beamtime_metadata.cpp
index 5d91a1fd8..dc1c1f1ef 100644
--- a/tests/automatic/producer/beamtime_metadata/beamtime_metadata.cpp
+++ b/tests/automatic/producer/beamtime_metadata/beamtime_metadata.cpp
@@ -21,7 +21,6 @@ void PrintCommandArguments(const Args& args) {
 }
 
 void ProcessCommandArguments(int argc, char* argv[], Args* args) {
-    asapo::ExitAfterPrintVersionIfNeeded("dummy beamtime metadata", argc, argv);
     if (argc != 4) {
         std::cout <<
                   "Usage: " << argv[0] <<
diff --git a/tests/manual/performance_broker_receiver/getlast_broker.cpp b/tests/manual/performance_broker_receiver/getlast_broker.cpp
index 59011a35a..f6a3c1a92 100644
--- a/tests/manual/performance_broker_receiver/getlast_broker.cpp
+++ b/tests/manual/performance_broker_receiver/getlast_broker.cpp
@@ -169,7 +169,6 @@ int ReadAllData(const Args& params, uint64_t* duration_ms, int* nerrors, int* nb
 }
 
 int main(int argc, char* argv[]) {
-    asapo::ExitAfterPrintVersionIfNeeded("GetLast consumer Example", argc, argv);
     Args params;
     params.datasets = false;
     if (argc != 9 && argc != 10) {
diff --git a/tests/manual/producer_cpp/producer.cpp b/tests/manual/producer_cpp/producer.cpp
index c198a7506..03607b938 100644
--- a/tests/manual/producer_cpp/producer.cpp
+++ b/tests/manual/producer_cpp/producer.cpp
@@ -1,7 +1,7 @@
 #include <thread>
 #include <chrono>
 #include "asapo/asapo_producer.h"
-
+#include <iostream>
 
 void ProcessAfterSend(asapo::RequestCallbackPayload payload, asapo::Error err) {
     if (err) {
-- 
GitLab