Files
infix/package/rousette/0030-Always-print-empty-containers-in-user-defined-conten.patch
T

357 lines
11 KiB
Diff

From 22442d322bda3d83457dd0f1da45d95a10e93081 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz>
Date: Thu, 13 Mar 2025 17:32:59 +0100
Subject: [PATCH 30/44] Always print empty containers in user-defined content
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
There's some subtle interaction between depth-limited printing, default
values, and empty non-presence containers. In order to prepare for an
upstream change in sysrepo which fixed depth-limited printing, let's
stick to "always" printing the empty non-presence containers.
(It actually isn't "always" because the data such as error reports,
patches, etc., are still printed using the default settings. But the
data which is subject to user-defined constraints is now consistent.)
Change-Id: Ie2d6ad2e851d667e7e7582fcd0ac75dabce2c05e
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/restconf/Server.cpp | 4 +-
src/restconf/utils/yang.cpp | 2 +-
src/sr/AllEvents.cpp | 2 +-
src/sr/OpticalEvents.cpp | 2 +-
tests/restconf-defaults.cpp | 12 +++++-
tests/restconf-nacm.cpp | 12 +++++-
tests/restconf-reading.cpp | 79 ++++++++++++++++++++++++++++++++++---
7 files changed, 98 insertions(+), 15 deletions(-)
diff --git a/src/restconf/Server.cpp b/src/restconf/Server.cpp
index 55e504a..bae3992 100644
--- a/src/restconf/Server.cpp
+++ b/src/restconf/Server.cpp
@@ -478,7 +478,7 @@ void processActionOrRPC(std::shared_ptr<RequestContext> requestCtx, const std::c
contentType(requestCtx->dataFormat.response),
CORS,
});
- requestCtx->res.end(*envelope->printStr(requestCtx->dataFormat.response, libyang::PrintFlags::WithSiblings));
+ requestCtx->res.end(*envelope->printStr(requestCtx->dataFormat.response, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont));
}
void processPost(std::shared_ptr<RequestContext> requestCtx, const std::chrono::milliseconds timeout)
@@ -768,7 +768,7 @@ libyang::PrintFlags libyangPrintFlags(const libyang::DataNode& dataNode, const s
} catch(const libyang::Error& e) {
}
- libyang::PrintFlags ret = libyang::PrintFlags::WithSiblings;
+ libyang::PrintFlags ret = libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont;
if (!withDefaults && node && (node->schema().nodeType() == libyang::NodeType::Leaf || node->schema().nodeType() == libyang::NodeType::Leaflist) && node->asTerm().isImplicitDefault()) {
return ret | libyang::PrintFlags::WithDefaultsAll;
diff --git a/src/restconf/utils/yang.cpp b/src/restconf/utils/yang.cpp
index 30661fc..8acb160 100644
--- a/src/restconf/utils/yang.cpp
+++ b/src/restconf/utils/yang.cpp
@@ -111,7 +111,7 @@ std::string as_restconf_notification(const libyang::Context& ctx, libyang::DataF
envelope->insertChild(*eventTime);
envelope->insertChild(notification);
- auto res = *envelope->printStr(dataFormat, libyang::PrintFlags::WithSiblings);
+ auto res = *envelope->printStr(dataFormat, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont);
// notification node comes from sysrepo and sysrepo will free this; if not unlinked then envelope destructor would try to free this as well
notification.unlink();
diff --git a/src/sr/AllEvents.cpp b/src/sr/AllEvents.cpp
index 6e2a21c..2aca4c7 100644
--- a/src/sr/AllEvents.cpp
+++ b/src/sr/AllEvents.cpp
@@ -109,7 +109,7 @@ sysrepo::ErrorCode AllEvents::onChange(sysrepo::Session session, const std::stri
break;
}
};
- auto json = *copy.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings);
+ auto json = *copy.printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont);
spdlog::info("JSON: {}", json);
spdlog::warn("FULL JSON: {}",
*session.getData('/' + module + ":*")->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings));
diff --git a/src/sr/OpticalEvents.cpp b/src/sr/OpticalEvents.cpp
index 8c5feff..25906a8 100644
--- a/src/sr/OpticalEvents.cpp
+++ b/src/sr/OpticalEvents.cpp
@@ -15,7 +15,7 @@
namespace {
std::string dumpDataFrom(sysrepo::Session session, const std::string& module)
{
- return *session.getData('/' + module + ":*")->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings);
+ return *session.getData('/' + module + ":*")->printStr(libyang::DataFormat::JSON, libyang::PrintFlags::WithSiblings | libyang::PrintFlags::KeepEmptyCont);
}
}
diff --git a/tests/restconf-defaults.cpp b/tests/restconf-defaults.cpp
index d5c6004..89083f1 100644
--- a/tests/restconf-defaults.cpp
+++ b/tests/restconf-defaults.cpp
@@ -33,7 +33,11 @@ TEST_CASE("default handling")
// default value of /example:a/b/c/enabled is implicitly set so it should not be printed
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a/b/c", {}) == Response{200, jsonHeaders, R"({
-
+ "example:a": {
+ "b": {
+ "c": {}
+ }
+ }
}
)"});
@@ -88,7 +92,11 @@ TEST_CASE("default handling")
// default value is only there implicitly, so it should *not* be printed
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a/b/c", {}) == Response{200, jsonHeaders, R"({
-
+ "example:a": {
+ "b": {
+ "c": {}
+ }
+ }
}
)"});
}
diff --git a/tests/restconf-nacm.cpp b/tests/restconf-nacm.cpp
index 29d7723..51328f3 100644
--- a/tests/restconf-nacm.cpp
+++ b/tests/restconf-nacm.cpp
@@ -130,6 +130,9 @@ TEST_CASE("NACM")
"clock": {
"timezone-utc-offset": 2
},
+ "dns-resolver": {
+ "options": {}
+ },
"radius": {
"server": [
{
@@ -139,7 +142,8 @@ TEST_CASE("NACM")
"shared-secret": "shared-secret"
}
}
- ]
+ ],
+ "options": {}
}
}
}
@@ -207,6 +211,9 @@ TEST_CASE("NACM")
"clock": {
"timezone-utc-offset": 2
},
+ "dns-resolver": {
+ "options": {}
+ },
"radius": {
"server": [
{
@@ -216,7 +223,8 @@ TEST_CASE("NACM")
"shared-secret": "shared-secret"
}
}
- ]
+ ],
+ "options": {}
}
}
}
diff --git a/tests/restconf-reading.cpp b/tests/restconf-reading.cpp
index e709486..ffdb047 100644
--- a/tests/restconf-reading.cpp
+++ b/tests/restconf-reading.cpp
@@ -62,10 +62,22 @@ TEST_CASE("reading data")
// this relies on a NACM rule for anonymous access that filters out "a lot of stuff"
REQUIRE(get(RESTCONF_DATA_ROOT, {}) == Response{200, jsonHeaders, R"({
"example:top-level-leaf": "moo",
+ "example:tlc": {},
+ "example:a": {
+ "b": {
+ "c": {}
+ },
+ "b1": {},
+ "example-augment:b": {
+ "c": {}
+ }
+ },
+ "example:two-leafs": {},
"example:config-nonconfig": {
"config-node": "foo-config-true",
"nonconfig-node": "foo-config-false"
},
+ "example:ordered-lists": {},
"ietf-restconf-monitoring:restconf-state": {
"capabilities": {
"capability": [
@@ -107,10 +119,22 @@ TEST_CASE("reading data")
REQUIRE(get(RESTCONF_ROOT_DS("operational"), {}) == Response{200, jsonHeaders, R"({
"example:top-level-leaf": "moo",
+ "example:tlc": {},
+ "example:a": {
+ "b": {
+ "c": {}
+ },
+ "b1": {},
+ "example-augment:b": {
+ "c": {}
+ }
+ },
+ "example:two-leafs": {},
"example:config-nonconfig": {
"config-node": "foo-config-true",
"nonconfig-node": "foo-config-false"
},
+ "example:ordered-lists": {},
"ietf-restconf-monitoring:restconf-state": {
"capabilities": {
"capability": [
@@ -152,9 +176,21 @@ TEST_CASE("reading data")
REQUIRE(get(RESTCONF_ROOT_DS("running"), {}) == Response{200, jsonHeaders, R"({
"example:top-level-leaf": "moo",
+ "example:tlc": {},
+ "example:a": {
+ "b": {
+ "c": {}
+ },
+ "b1": {},
+ "example-augment:b": {
+ "c": {}
+ }
+ },
+ "example:two-leafs": {},
"example:config-nonconfig": {
"config-node": "foo-config-true"
- }
+ },
+ "example:ordered-lists": {}
}
)"});
}
@@ -213,7 +249,8 @@ TEST_CASE("reading data")
{
"name": "a"
}
- ]
+ ],
+ "options": {}
}
}
}
@@ -244,7 +281,8 @@ TEST_CASE("reading data")
"shared-secret": "shared-secret"
}
}
- ]
+ ],
+ "options": {}
}
}
}
@@ -714,6 +752,7 @@ TEST_CASE("reading data")
"enabled": true
}
},
+ "b1": {},
"example-augment:b": {
"c": {
"enabled": true
@@ -723,9 +762,18 @@ TEST_CASE("reading data")
}
)"});
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a?with-defaults=explicit", {}) == Response{200, jsonHeaders, R"({
-
+ "example:a": {
+ "b": {
+ "c": {}
+ },
+ "b1": {},
+ "example-augment:b": {
+ "c": {}
+ }
+ }
}
)"});
+ // FIXME: libyang is not really consistent in printing of NP-containers when trimming away the defaults...
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a?with-defaults=trim", {}) == Response{200, jsonHeaders, R"({
}
@@ -740,6 +788,7 @@ TEST_CASE("reading data")
}
}
},
+ "b1": {},
"example-augment:b": {
"c": {
"enabled": true,
@@ -766,6 +815,7 @@ TEST_CASE("reading data")
"enabled": true
}
},
+ "b1": {},
"example-augment:b": {
"c": {
"enabled": true
@@ -781,13 +831,24 @@ TEST_CASE("reading data")
"c": {
"enabled": true
}
+ },
+ "b1": {},
+ "example-augment:b": {
+ "c": {}
}
}
}
)"});
+ // FIXME: libyang is not consistent here:
+ // - /example:a/b/c NP-container *is* printed,
+ // - /example:a/example-augment:b/c NP-container is *not* printed
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a?with-defaults=trim", {}) == Response{200, jsonHeaders, R"({
-
+ "example:a": {
+ "b": {
+ "c": {}
+ }
+ }
}
)"});
@@ -801,6 +862,7 @@ TEST_CASE("reading data")
}
}
},
+ "b1": {},
"example-augment:b": {
"c": {
"enabled": true,
@@ -830,10 +892,15 @@ TEST_CASE("reading data")
)"});
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a/b/c/enabled?with-defaults=explicit", {}) == Response{200, jsonHeaders, R"({
-
+ "example:a": {
+ "b": {
+ "c": {}
+ }
+ }
}
)"});
+ // again, libyang is not 100% consistent in the `trim` mode
REQUIRE(get(RESTCONF_DATA_ROOT "/example:a/b/c/enabled?with-defaults=trim", {}) == Response{200, jsonHeaders, R"({
}
--
2.43.0