From 27dc076cbdff3c9edf00abb05bdce59d1944988b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
Date: Fri, 31 Jan 2025 17:54:06 +0100
Subject: [PATCH 08/20] upstream break: sr_rpc_send_tree's `output` might be
 NULL now
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires

As of upstream commit 927fd5221bd28ff50cc1ee7c2cb5c5f30b34c2c9, sysrepo
can now return a nullptr when the resulting tree is empty. This appears
to have been done in response to our bugreport where we pointed out that
`netopeer2-server` started crashing on empty RPCs. However, it's unclear
why this necessitated a change in sysrepo, given that this feature has
worked well for years.

Anyway, let's propagate this through the C++ layer; what else can I do,
after all.

Bug: https://github.com/CESNET/netopeer2/issues/1695
Change-Id: I646d19f7448d98f0d21e9eec006f28fbb5e479fc
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
 .zuul.yaml                      | 4 ++--
 CMakeLists.txt                  | 4 ++--
 include/sysrepo-cpp/Session.hpp | 2 +-
 src/Session.cpp                 | 6 ++++--
 tests/subscriptions.cpp         | 7 ++++---
 5 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/.zuul.yaml b/.zuul.yaml
index 70c5e49..dc87c6f 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -6,7 +6,7 @@
               - name: github/CESNET/libyang
                 override-checkout: devel
               - name: github/sysrepo/sysrepo
-                override-checkout: cesnet/2025-01-30--00
+                override-checkout: devel
               - name: github/onqtam/doctest
                 override-checkout: v2.4.8
               - name: github/rollbear/trompeloeil
@@ -17,7 +17,7 @@
               - name: github/CESNET/libyang
                 override-checkout: devel
               - name: github/sysrepo/sysrepo
-                override-checkout: cesnet/2025-01-30--00
+                override-checkout: devel
               - name: github/onqtam/doctest
                 override-checkout: v2.4.11
               - name: github/rollbear/trompeloeil
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9e09d3d..85ff84c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,7 +20,7 @@ add_custom_target(sysrepo-cpp-version-cmake
         cmake/ProjectGitVersionRunner.cmake
         )
 include(cmake/ProjectGitVersion.cmake)
-set(SYSREPO_CPP_PKG_VERSION "4")
+set(SYSREPO_CPP_PKG_VERSION "5")
 prepare_git_version(SYSREPO_CPP_VERSION ${SYSREPO_CPP_PKG_VERSION})
 
 find_package(Doxygen)
@@ -29,7 +29,7 @@ option(WITH_EXAMPLES "Build examples" ON)
 
 find_package(PkgConfig)
 pkg_check_modules(LIBYANG_CPP REQUIRED libyang-cpp>=3 IMPORTED_TARGET)
-pkg_check_modules(SYSREPO REQUIRED sysrepo>=3.4.7 IMPORTED_TARGET)
+pkg_check_modules(SYSREPO REQUIRED sysrepo>=3.4.8 IMPORTED_TARGET)
 
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
 
diff --git a/include/sysrepo-cpp/Session.hpp b/include/sysrepo-cpp/Session.hpp
index 238f480..6e31fa2 100644
--- a/include/sysrepo-cpp/Session.hpp
+++ b/include/sysrepo-cpp/Session.hpp
@@ -95,7 +95,7 @@ public:
     void discardOperationalChanges(const std::optional<std::string>& moduleName = std::nullopt, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
     void dropForeignOperationalContent(const std::optional<std::string>& xpath);
     void copyConfig(const Datastore source, const std::optional<std::string>& moduleName = std::nullopt, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
-    libyang::DataNode sendRPC(libyang::DataNode input, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
+    std::optional<libyang::DataNode> sendRPC(libyang::DataNode input, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
     void sendNotification(libyang::DataNode notification, const Wait wait, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
     void replaceConfig(std::optional<libyang::DataNode> config, const std::optional<std::string>& moduleName = std::nullopt, std::chrono::milliseconds timeout = std::chrono::milliseconds{0});
 
diff --git a/src/Session.cpp b/src/Session.cpp
index 634e450..5df04bc 100644
--- a/src/Session.cpp
+++ b/src/Session.cpp
@@ -343,13 +343,15 @@ void Session::copyConfig(const Datastore source, const std::optional<std::string
  * @param input Libyang tree representing the RPC/action.
  * @param timeout Optional timeout.
  */
-libyang::DataNode Session::sendRPC(libyang::DataNode input, std::chrono::milliseconds timeout)
+std::optional<libyang::DataNode> Session::sendRPC(libyang::DataNode input, std::chrono::milliseconds timeout)
 {
     sr_data_t* output;
     auto res = sr_rpc_send_tree(m_sess.get(), libyang::getRawNode(input), timeout.count(), &output);
     throwIfError(res, "Couldn't send RPC", m_sess.get());
 
-    assert(output); // TODO: sysrepo always gives the RPC node? (even when it has not output or output nodes?)
+    if (!output) {
+        return std::nullopt;
+    }
     return wrapSrData(m_sess, output);
 }
 
diff --git a/tests/subscriptions.cpp b/tests/subscriptions.cpp
index 36fd61c..6fc9cbb 100644
--- a/tests/subscriptions.cpp
+++ b/tests/subscriptions.cpp
@@ -420,9 +420,10 @@ TEST_CASE("subscriptions")
         if (ret == sysrepo::ErrorCode::Ok) {
             auto output = sess.sendRPC(sess.getContext().newPath(rpcPath));
             if (setFunction) {
-                REQUIRE(output.findPath("/test_module:shutdown/success", libyang::InputOutputNodes::Output));
-            } else {
-                REQUIRE(!output.findPath("/test_module:shutdown/success", libyang::InputOutputNodes::Output).has_value());
+                REQUIRE(!!output);
+                REQUIRE(output->findPath("/test_module:shutdown/success", libyang::InputOutputNodes::Output));
+            } else if (output) {
+                REQUIRE(!output->findPath("/test_module:shutdown/success", libyang::InputOutputNodes::Output).has_value());
             }
         } else {
             REQUIRE_THROWS(sess.sendRPC(sess.getContext().newPath(rpcPath)));
-- 
2.43.0

