Add set, get and listkeys -sdltool CLI commands

Added new sdltool CLI commands:

 listkeys to list keys matching search pattern under the namespace.
 get to read data from storage under the namespace.
 set to write data to storage under the namespace.

Release version 1.5.1

Issue-Id: RIC-110
Change-Id: I6ce4b355e8a1d62e30bd4ffa79216318669915f8
Signed-off-by: Petri Ovaska <petri.ovaska@nokia.com>
diff --git a/Makefile.am b/Makefile.am
index bb92ba9..9a1ae21 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -141,6 +141,9 @@
     src/cli/dumpconfigurationcommand.cpp \
     src/cli/testgetsetcommand.cpp \
     src/cli/testconnectivitycommand.cpp \
+    src/cli/listkeyscommand.cpp \
+    src/cli/setcommand.cpp \
+    src/cli/getcommand.cpp \
     src/exception.cpp \
     src/configurationpaths.cpp \
     include/private/configurationpaths.hpp \
diff --git a/configure.ac b/configure.ac
index 97965fe..ab652d4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,7 +11,7 @@
 
 m4_define([SDL_MAJOR], [1])
 m4_define([SDL_MINOR], [5])
-m4_define([SDL_MICRO], [0])
+m4_define([SDL_MICRO], [1])
 
 # SDL ABI version with libtool
 #
@@ -28,7 +28,7 @@
 # Change the numbers just before release.
 
 m4_define([SDL_CURRENT], [5])
-m4_define([SDL_REVISION], [12])
+m4_define([SDL_REVISION], [13])
 m4_define([SDL_AGE], [0])
 
 AC_INIT([shareddatalayer], [SDL_MAJOR.SDL_MINOR.SDL_MICRO], [], [], [https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/sdl])
diff --git a/debian/changelog.in b/debian/changelog.in
index 78190df..b29304a 100644
--- a/debian/changelog.in
+++ b/debian/changelog.in
@@ -1,3 +1,9 @@
+sdl (1.5.1-1) UNRELEASED; urgency=low
+
+  * Add set, get and listKeys -sdltool CLI commands
+
+ -- Petri Ovaska <petri.ovaska@nokia.com>  Fri, 17 Sep 2021 12:33:24 +0300
+
 sdl (1.5.0-1) UNRELEASED; urgency=low
 
   * New listKeys API to support glob-style search pattern
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
index 6654d05..c4913ac 100644
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -29,6 +29,10 @@
 Version history
 ---------------
 
+[1.5.1] - 2021-09-17
+
+* Add set, get and listKeys -sdltool CLI commands
+
 [1.5.0] - 2021-09-17
 
 * New listKeys API to support glob-style search pattern
diff --git a/include/private/cli/common.hpp b/include/private/cli/common.hpp
new file mode 100644
index 0000000..e213b41
--- /dev/null
+++ b/include/private/cli/common.hpp
@@ -0,0 +1,27 @@
+#ifndef SHAREDDATALAYER_CLI_COMMON_HPP
+#define SHAREDDATALAYER_CLI_COMMON_HPP
+
+#include <functional>
+#include <iostream>
+#include <sdl/syncstorage.hpp>
+
+namespace shareddatalayer
+{
+    namespace cli
+    {
+        inline std::ostream& operator<<(std::ostream &out, const SyncStorage::Data &data)
+        {
+            for (const auto& d : data)
+                out << d;
+            return out;
+        }
+
+        inline std::ostream& operator<<(std::ostream& out, const SyncStorage::DataMap& dataMap)
+        {
+            for (const auto& d : dataMap)
+                out << d.first << " " << d.second;
+            return out;
+        }
+    }
+}
+#endif
diff --git a/rpm/sdl.spec.in b/rpm/sdl.spec.in
index b9c8378..3b019b3 100755
--- a/rpm/sdl.spec.in
+++ b/rpm/sdl.spec.in
@@ -1,5 +1,5 @@
 Name:     sdl
-Version:  1.5.0
+Version:  1.5.1
 Release:  1%{?dist}
 Summary:  C++ API library for Shared Data Layer clients
 
@@ -50,6 +50,9 @@
 %{_includedir}/sdl
 
 %changelog
+* Fri Sep 17 2021 Petri Ovaska <petri.ovaska@nokia.com> - 1.5.1-1
+- Add set, get and listKeys -sdltool CLI commands
+
 * Fri Sep 17 2021 Petri Ovaska <petri.ovaska@nokia.com> - 1.5.0-1
 - New listKeys API to support glob-style search pattern
 - Deprecated old findKeys and findKeysAsync API
diff --git a/src/cli/getcommand.cpp b/src/cli/getcommand.cpp
new file mode 100644
index 0000000..2c34fd4
--- /dev/null
+++ b/src/cli/getcommand.cpp
@@ -0,0 +1,75 @@
+#include <ostream>
+#include <cstdlib>
+#include "private/cli/commandmap.hpp"
+#include "private/cli/common.hpp"
+#include <sdl/syncstorage.hpp>
+
+using namespace shareddatalayer;
+using namespace shareddatalayer::cli;
+
+namespace
+{
+    std::shared_ptr<shareddatalayer::SyncStorage> createSyncStorage(const SyncStorage::Namespace& ns,
+                                                                    std::ostream& out)
+    {
+        try
+        {
+            auto sdl(shareddatalayer::SyncStorage::create());
+            sdl->waitReady(ns, std::chrono::minutes(1));
+            return sdl;
+        }
+        catch (const shareddatalayer::Exception& error)
+        {
+            out << "SyncStorage create failed: " << error.what() << std::endl;
+        }
+
+        return nullptr;
+    }
+
+    void get(shareddatalayer::SyncStorage& sdl,
+             const SyncStorage::Namespace& ns,
+             const SyncStorage::Key& key,
+             std::ostream& out)
+    {
+        try
+        {
+            auto data(sdl.get(ns, {key}));
+            out << data << std::endl;
+        }
+        catch (const shareddatalayer::Exception& error)
+        {
+            out << "get(" << ns << ", " << key << ") failed: "
+                << error.what() << std::endl;
+        }
+    }
+
+    int getCommand(std::ostream& out, const boost::program_options::variables_map& map)
+    {
+        auto ns(map["ns"].as<std::string>());
+        auto key(map["key"].as<std::string>());
+
+        auto sdl(createSyncStorage(ns, out));
+        if (nullptr == sdl)
+            return EXIT_FAILURE;
+        sdl->setOperationTimeout(std::chrono::seconds(5));
+
+        get(std::ref(*sdl), ns, key, out);
+
+        return EXIT_SUCCESS;
+    }
+}
+
+const char *longHelpGetCmd =
+        "Use get SDL API to read data from storage under the namespace.\n\n"
+        "Example: sdltool get --ns 'sdltool' --key 'key1'";
+
+AUTO_REGISTER_COMMAND(std::bind(getCommand, std::placeholders::_1, std::placeholders::_3),
+                      "get",
+                      "get data with SDL API under the namespace",
+                      longHelpGetCmd,
+                      CommandMap::Category::UTIL,
+                      30050,
+                      ("ns,n", boost::program_options::value<std::string>()->default_value("sdltoolns"), "namespace to use")
+                      ("key,k", boost::program_options::value<std::string>()->default_value("key1"), "key value")
+                     );
+
diff --git a/src/cli/listkeyscommand.cpp b/src/cli/listkeyscommand.cpp
new file mode 100644
index 0000000..04f39c7
--- /dev/null
+++ b/src/cli/listkeyscommand.cpp
@@ -0,0 +1,77 @@
+#include <ostream>
+#include <cstdlib>
+#include "private/cli/commandmap.hpp"
+#include "private/cli/common.hpp"
+#include <sdl/syncstorage.hpp>
+#include <sdl/asyncstorage.hpp>
+
+using namespace shareddatalayer;
+using namespace shareddatalayer::cli;
+
+namespace
+{
+    std::shared_ptr<shareddatalayer::SyncStorage> createSyncStorage(const SyncStorage::Namespace& ns,
+                                                                    std::ostream& out)
+    {
+        try
+        {
+            auto sdl(shareddatalayer::SyncStorage::create());
+            sdl->waitReady(ns, std::chrono::minutes(1));
+            return sdl;
+        }
+        catch (const shareddatalayer::Exception& error)
+        {
+            out << "SyncStorage create failed: " << error.what() << std::endl;
+        }
+
+        return nullptr;
+    }
+
+    void listkeys(shareddatalayer::SyncStorage& sdl,
+                  const SyncStorage::Namespace& ns,
+                  const std::string& pattern,
+                  std::ostream& out)
+    {
+        try
+        {
+            auto keys(sdl.listKeys(ns, pattern));
+            for (auto key: keys)
+                out << key << std::endl;
+        }
+        catch (const shareddatalayer::Exception& error)
+        {
+            out << "listKeys(" << ns << ", " << pattern << ") failed: "
+                << error.what() << std::endl;
+        }
+    }
+
+    int listKeysCommand(std::ostream& out, const boost::program_options::variables_map& map)
+    {
+        auto ns(map["ns"].as<std::string>());
+        auto pattern(map["pattern"].as<std::string>());
+
+        auto sdl(createSyncStorage(ns, out));
+        if (nullptr == sdl)
+            return EXIT_FAILURE;
+        sdl->setOperationTimeout(std::chrono::seconds(5));
+
+        listkeys(std::ref(*sdl), ns, pattern, out);
+
+        return EXIT_SUCCESS;
+    }
+}
+
+const char *longHelpListkeysCmd =
+    "Use listKeys SDL API to list keys matching search pattern under the namespace.\n\n"
+    "Example: sdltool listkeys --ns 'sdltool' --pattern 'foo*'";
+
+AUTO_REGISTER_COMMAND(std::bind(listKeysCommand, std::placeholders::_1, std::placeholders::_3),
+                      "listkeys",
+                      "listKeys SDL API",
+                      longHelpListkeysCmd,
+                      CommandMap::Category::UTIL,
+                      30030,
+                      ("ns,n", boost::program_options::value<std::string>()->default_value("sdltoolns"), "namespace to use")
+                      ("pattern,p", boost::program_options::value<std::string>()->default_value("*"), "key search pattern")
+                     );
+
diff --git a/src/cli/setcommand.cpp b/src/cli/setcommand.cpp
new file mode 100644
index 0000000..425be4d
--- /dev/null
+++ b/src/cli/setcommand.cpp
@@ -0,0 +1,87 @@
+#include <ostream>
+#include <cstdlib>
+#include "private/cli/commandmap.hpp"
+#include "private/cli/common.hpp"
+#include <sdl/syncstorage.hpp>
+
+using namespace shareddatalayer;
+using namespace shareddatalayer::cli;
+
+namespace
+{
+    std::shared_ptr<shareddatalayer::SyncStorage> createSyncStorage(const SyncStorage::Namespace& ns,
+                                                                    std::ostream& out)
+    {
+        try
+        {
+            auto sdl(shareddatalayer::SyncStorage::create());
+            sdl->waitReady(ns, std::chrono::minutes(1));
+            return sdl;
+        }
+        catch (const shareddatalayer::Exception& error)
+        {
+            out << "SyncStorage create failed: " << error.what() << std::endl;
+        }
+
+        return nullptr;
+    }
+
+    void set(shareddatalayer::SyncStorage& sdl,
+             const SyncStorage::Namespace& ns,
+             const SyncStorage::DataMap& dataMap,
+             std::ostream& out)
+    {
+        try
+        {
+            sdl.set(ns, dataMap);
+            out << "set {" << ns << "}," << dataMap << std::endl;
+        }
+        catch (const shareddatalayer::Exception& error)
+        {
+            out << "Error in set(" << ns << ", " << dataMap << ") failed: "
+                << error.what() << std::endl;
+        }
+    }
+
+    int setCommand(std::ostream& out, const boost::program_options::variables_map& map)
+    {
+        auto ns(map["ns"].as<std::string>());
+        auto key(map["key"].as<std::string>());
+        auto data(map["data"].as<std::string>());
+        auto debug(map["debug"].as<bool>());
+        SyncStorage::Data dataVector(data.begin(), data.end());
+
+        auto sdl(createSyncStorage(ns, out));
+        if (nullptr == sdl)
+            return EXIT_FAILURE;
+        sdl->setOperationTimeout(std::chrono::seconds(5));
+
+        if (debug)
+        {
+            out << "DEBUG setCommand ns: " << ns
+                << " key: " << key << " data: " << data << std::endl;
+        }
+
+        SyncStorage::DataMap datamap({{key, dataVector}});
+        set(std::ref(*sdl), ns, datamap, out);
+
+        return EXIT_SUCCESS;
+    }
+}
+
+const char *longHelpSetCmd =
+    "Use set SDL API to write data to storage under the namespace.\n\n"
+    "Example: sdltool set --ns 'sdltool' --key 'key1' --data '1'";
+
+AUTO_REGISTER_COMMAND(std::bind(setCommand, std::placeholders::_1, std::placeholders::_3),
+                      "set",
+                      "set key value with SDL API",
+                      longHelpSetCmd,
+                      CommandMap::Category::UTIL,
+                      30040,
+                      ("ns,n", boost::program_options::value<std::string>()->default_value("sdltoolns"), "namespace to use")
+                      ("key,k", boost::program_options::value<std::string>()->default_value("key1"), "key value")
+                      ("data,d", boost::program_options::value<std::string>()->default_value("1"), "data")
+                      ("debug", boost::program_options::bool_switch()->default_value(false), "Enable debug logs")
+                     );
+